Path: blob/master/drivers/gpu/drm/bridge/lontium-lt8912b.c
26494 views
// SPDX-License-Identifier: GPL-2.01/*2* Copyright (c) 2018, The Linux Foundation. All rights reserved.3*/45#include <linux/device.h>6#include <linux/delay.h>7#include <linux/gpio/consumer.h>8#include <linux/i2c.h>9#include <linux/media-bus-format.h>10#include <linux/regmap.h>1112#include <drm/drm_probe_helper.h>13#include <drm/drm_atomic_helper.h>14#include <drm/drm_edid.h>15#include <drm/drm_mipi_dsi.h>16#include <drm/drm_of.h>1718#include <video/videomode.h>1920#define I2C_MAIN 021#define I2C_ADDR_MAIN 0x482223#define I2C_CEC_DSI 124#define I2C_ADDR_CEC_DSI 0x492526#define I2C_MAX_IDX 22728struct lt8912 {29struct device *dev;30struct drm_bridge bridge;31struct drm_connector connector;3233struct i2c_client *i2c_client[I2C_MAX_IDX];34struct regmap *regmap[I2C_MAX_IDX];3536struct device_node *host_node;37struct drm_bridge *hdmi_port;3839struct mipi_dsi_device *dsi;4041struct gpio_desc *gp_reset;4243struct videomode mode;4445struct regulator_bulk_data supplies[7];4647u8 data_lanes;48bool is_power_on;49};5051static int lt8912_write_init_config(struct lt8912 *lt)52{53const struct reg_sequence seq[] = {54/* Digital clock en*/55{0x08, 0xff},56{0x09, 0xff},57{0x0a, 0xff},58{0x0b, 0x7c},59{0x0c, 0xff},60{0x42, 0x04},6162/*Tx Analog*/63{0x31, 0xb1},64{0x32, 0xb1},65{0x33, 0x0e},66{0x37, 0x00},67{0x38, 0x22},68{0x60, 0x82},6970/*Cbus Analog*/71{0x39, 0x45},72{0x3a, 0x00},73{0x3b, 0x00},7475/*HDMI Pll Analog*/76{0x44, 0x31},77{0x55, 0x44},78{0x57, 0x01},79{0x5a, 0x02},8081/*MIPI Analog*/82{0x3e, 0xd6},83{0x3f, 0xd4},84{0x41, 0x3c},85{0xB2, 0x00},86};8788return regmap_multi_reg_write(lt->regmap[I2C_MAIN], seq, ARRAY_SIZE(seq));89}9091static int lt8912_write_mipi_basic_config(struct lt8912 *lt)92{93const struct reg_sequence seq[] = {94{0x12, 0x04},95{0x14, 0x00},96{0x15, 0x00},97{0x1a, 0x03},98{0x1b, 0x03},99};100101return regmap_multi_reg_write(lt->regmap[I2C_CEC_DSI], seq, ARRAY_SIZE(seq));102};103104static int lt8912_write_dds_config(struct lt8912 *lt)105{106const struct reg_sequence seq[] = {107{0x4e, 0xff},108{0x4f, 0x56},109{0x50, 0x69},110{0x51, 0x80},111{0x1f, 0x5e},112{0x20, 0x01},113{0x21, 0x2c},114{0x22, 0x01},115{0x23, 0xfa},116{0x24, 0x00},117{0x25, 0xc8},118{0x26, 0x00},119{0x27, 0x5e},120{0x28, 0x01},121{0x29, 0x2c},122{0x2a, 0x01},123{0x2b, 0xfa},124{0x2c, 0x00},125{0x2d, 0xc8},126{0x2e, 0x00},127{0x42, 0x64},128{0x43, 0x00},129{0x44, 0x04},130{0x45, 0x00},131{0x46, 0x59},132{0x47, 0x00},133{0x48, 0xf2},134{0x49, 0x06},135{0x4a, 0x00},136{0x4b, 0x72},137{0x4c, 0x45},138{0x4d, 0x00},139{0x52, 0x08},140{0x53, 0x00},141{0x54, 0xb2},142{0x55, 0x00},143{0x56, 0xe4},144{0x57, 0x0d},145{0x58, 0x00},146{0x59, 0xe4},147{0x5a, 0x8a},148{0x5b, 0x00},149{0x5c, 0x34},150{0x1e, 0x4f},151{0x51, 0x00},152};153154return regmap_multi_reg_write(lt->regmap[I2C_CEC_DSI], seq, ARRAY_SIZE(seq));155}156157static int lt8912_write_rxlogicres_config(struct lt8912 *lt)158{159int ret;160161ret = regmap_write(lt->regmap[I2C_MAIN], 0x03, 0x7f);162usleep_range(10000, 20000);163ret |= regmap_write(lt->regmap[I2C_MAIN], 0x03, 0xff);164165return ret;166};167168/* enable LVDS output with some hardcoded configuration, not required for the HDMI output */169static int lt8912_write_lvds_config(struct lt8912 *lt)170{171const struct reg_sequence seq[] = {172// lvds power up173{0x44, 0x30},174{0x51, 0x05},175176// core pll bypass177{0x50, 0x24}, // cp=50uA178{0x51, 0x2d}, // Pix_clk as reference, second order passive LPF PLL179{0x52, 0x04}, // loopdiv=0, use second-order PLL180{0x69, 0x0e}, // CP_PRESET_DIV_RATIO181{0x69, 0x8e},182{0x6a, 0x00},183{0x6c, 0xb8}, // RGD_CP_SOFT_K_EN,RGD_CP_SOFT_K[13:8]184{0x6b, 0x51},185186{0x04, 0xfb}, // core pll reset187{0x04, 0xff},188189// scaler bypass190{0x7f, 0x00}, // disable scaler191{0xa8, 0x13}, // 0x13: JEIDA, 0x33: VESA192193{0x02, 0xf7}, // lvds pll reset194{0x02, 0xff},195{0x03, 0xcf},196{0x03, 0xff},197};198199return regmap_multi_reg_write(lt->regmap[I2C_MAIN], seq, ARRAY_SIZE(seq));200};201202static inline struct lt8912 *bridge_to_lt8912(struct drm_bridge *b)203{204return container_of(b, struct lt8912, bridge);205}206207static inline struct lt8912 *connector_to_lt8912(struct drm_connector *c)208{209return container_of(c, struct lt8912, connector);210}211212static const struct regmap_config lt8912_regmap_config = {213.reg_bits = 8,214.val_bits = 8,215.max_register = 0xff,216};217218static int lt8912_init_i2c(struct lt8912 *lt, struct i2c_client *client)219{220unsigned int i;221/*222* At this time we only initialize 2 chips, but the lt8912 provides223* a third interface for the audio over HDMI configuration.224*/225struct i2c_board_info info[] = {226{ I2C_BOARD_INFO("lt8912p0", I2C_ADDR_MAIN), },227{ I2C_BOARD_INFO("lt8912p1", I2C_ADDR_CEC_DSI), },228};229230if (!lt)231return -ENODEV;232233for (i = 0; i < ARRAY_SIZE(info); i++) {234if (i > 0) {235lt->i2c_client[i] = i2c_new_dummy_device(client->adapter,236info[i].addr);237if (IS_ERR(lt->i2c_client[i]))238return PTR_ERR(lt->i2c_client[i]);239}240241lt->regmap[i] = devm_regmap_init_i2c(lt->i2c_client[i],242<8912_regmap_config);243if (IS_ERR(lt->regmap[i]))244return PTR_ERR(lt->regmap[i]);245}246return 0;247}248249static int lt8912_free_i2c(struct lt8912 *lt)250{251unsigned int i;252253for (i = 1; i < I2C_MAX_IDX; i++)254i2c_unregister_device(lt->i2c_client[i]);255256return 0;257}258259static int lt8912_hard_power_on(struct lt8912 *lt)260{261int ret;262263ret = regulator_bulk_enable(ARRAY_SIZE(lt->supplies), lt->supplies);264if (ret)265return ret;266267gpiod_set_value_cansleep(lt->gp_reset, 0);268msleep(20);269270return 0;271}272273static void lt8912_hard_power_off(struct lt8912 *lt)274{275gpiod_set_value_cansleep(lt->gp_reset, 1);276msleep(20);277278regulator_bulk_disable(ARRAY_SIZE(lt->supplies), lt->supplies);279280lt->is_power_on = false;281}282283static int lt8912_video_setup(struct lt8912 *lt)284{285u32 hactive, h_total, hpw, hfp, hbp;286u32 vactive, v_total, vpw, vfp, vbp;287u8 settle = 0x08;288int ret, hsync_activehigh, vsync_activehigh;289290if (!lt)291return -EINVAL;292293hactive = lt->mode.hactive;294hfp = lt->mode.hfront_porch;295hpw = lt->mode.hsync_len;296hbp = lt->mode.hback_porch;297h_total = hactive + hfp + hpw + hbp;298hsync_activehigh = lt->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH;299300vactive = lt->mode.vactive;301vfp = lt->mode.vfront_porch;302vpw = lt->mode.vsync_len;303vbp = lt->mode.vback_porch;304v_total = vactive + vfp + vpw + vbp;305vsync_activehigh = lt->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH;306307if (vactive <= 600)308settle = 0x04;309else if (vactive == 1080)310settle = 0x0a;311312ret = regmap_write(lt->regmap[I2C_CEC_DSI], 0x10, 0x01);313ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x11, settle);314ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x18, hpw);315ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x19, vpw);316ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x1c, hactive & 0xff);317ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x1d, hactive >> 8);318319ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x2f, 0x0c);320321ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x34, h_total & 0xff);322ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x35, h_total >> 8);323324ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x36, v_total & 0xff);325ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x37, v_total >> 8);326327ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x38, vbp & 0xff);328ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x39, vbp >> 8);329330ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x3a, vfp & 0xff);331ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x3b, vfp >> 8);332333ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x3c, hbp & 0xff);334ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x3d, hbp >> 8);335336ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x3e, hfp & 0xff);337ret |= regmap_write(lt->regmap[I2C_CEC_DSI], 0x3f, hfp >> 8);338339ret |= regmap_update_bits(lt->regmap[I2C_MAIN], 0xab, BIT(0),340vsync_activehigh ? BIT(0) : 0);341ret |= regmap_update_bits(lt->regmap[I2C_MAIN], 0xab, BIT(1),342hsync_activehigh ? BIT(1) : 0);343ret |= regmap_update_bits(lt->regmap[I2C_MAIN], 0xb2, BIT(0),344lt->connector.display_info.is_hdmi ? BIT(0) : 0);345346return ret;347}348349static int lt8912_soft_power_on(struct lt8912 *lt)350{351if (!lt->is_power_on) {352u32 lanes = lt->data_lanes;353354lt8912_write_init_config(lt);355regmap_write(lt->regmap[I2C_CEC_DSI], 0x13, lanes & 3);356357lt8912_write_mipi_basic_config(lt);358359lt->is_power_on = true;360}361362return 0;363}364365static int lt8912_video_on(struct lt8912 *lt)366{367int ret;368369ret = lt8912_video_setup(lt);370if (ret < 0)371goto end;372373ret = lt8912_write_dds_config(lt);374if (ret < 0)375goto end;376377ret = lt8912_write_rxlogicres_config(lt);378if (ret < 0)379goto end;380381ret = lt8912_write_lvds_config(lt);382if (ret < 0)383goto end;384385end:386return ret;387}388389static enum drm_connector_status lt8912_check_cable_status(struct lt8912 *lt)390{391int ret;392unsigned int reg_val;393394ret = regmap_read(lt->regmap[I2C_MAIN], 0xC1, ®_val);395if (ret)396return connector_status_unknown;397398if (reg_val & BIT(7))399return connector_status_connected;400401return connector_status_disconnected;402}403404static enum drm_connector_status405lt8912_connector_detect(struct drm_connector *connector, bool force)406{407struct lt8912 *lt = connector_to_lt8912(connector);408409if (lt->hdmi_port->ops & DRM_BRIDGE_OP_DETECT)410return drm_bridge_detect(lt->hdmi_port, connector);411412return lt8912_check_cable_status(lt);413}414415static const struct drm_connector_funcs lt8912_connector_funcs = {416.detect = lt8912_connector_detect,417.fill_modes = drm_helper_probe_single_connector_modes,418.destroy = drm_connector_cleanup,419.reset = drm_atomic_helper_connector_reset,420.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,421.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,422};423424static int lt8912_connector_get_modes(struct drm_connector *connector)425{426const struct drm_edid *drm_edid;427struct lt8912 *lt = connector_to_lt8912(connector);428u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;429int ret, num;430431drm_edid = drm_bridge_edid_read(lt->hdmi_port, connector);432drm_edid_connector_update(connector, drm_edid);433if (!drm_edid)434return 0;435436num = drm_edid_connector_add_modes(connector);437438ret = drm_display_info_set_bus_formats(&connector->display_info,439&bus_format, 1);440if (ret < 0)441num = 0;442443drm_edid_free(drm_edid);444return num;445}446447static const struct drm_connector_helper_funcs lt8912_connector_helper_funcs = {448.get_modes = lt8912_connector_get_modes,449};450451static void lt8912_bridge_mode_set(struct drm_bridge *bridge,452const struct drm_display_mode *mode,453const struct drm_display_mode *adj)454{455struct lt8912 *lt = bridge_to_lt8912(bridge);456457drm_display_mode_to_videomode(adj, <->mode);458}459460static void lt8912_bridge_enable(struct drm_bridge *bridge)461{462struct lt8912 *lt = bridge_to_lt8912(bridge);463464lt8912_video_on(lt);465}466467static int lt8912_attach_dsi(struct lt8912 *lt)468{469struct device *dev = lt->dev;470struct mipi_dsi_host *host;471struct mipi_dsi_device *dsi;472int ret = -1;473const struct mipi_dsi_device_info info = { .type = "lt8912",474.channel = 0,475.node = NULL,476};477478host = of_find_mipi_dsi_host_by_node(lt->host_node);479if (!host)480return dev_err_probe(dev, -EPROBE_DEFER, "failed to find dsi host\n");481482dsi = devm_mipi_dsi_device_register_full(dev, host, &info);483if (IS_ERR(dsi)) {484ret = PTR_ERR(dsi);485dev_err(dev, "failed to create dsi device (%d)\n", ret);486return ret;487}488489lt->dsi = dsi;490491dsi->lanes = lt->data_lanes;492dsi->format = MIPI_DSI_FMT_RGB888;493494dsi->mode_flags = MIPI_DSI_MODE_VIDEO |495MIPI_DSI_MODE_LPM |496MIPI_DSI_MODE_NO_EOT_PACKET;497498ret = devm_mipi_dsi_attach(dev, dsi);499if (ret < 0) {500dev_err(dev, "failed to attach dsi to host\n");501return ret;502}503504return 0;505}506507static void lt8912_bridge_hpd_cb(void *data, enum drm_connector_status status)508{509struct lt8912 *lt = data;510511if (lt->bridge.dev)512drm_helper_hpd_irq_event(lt->bridge.dev);513}514515static int lt8912_bridge_connector_init(struct drm_bridge *bridge)516{517int ret;518struct lt8912 *lt = bridge_to_lt8912(bridge);519struct drm_connector *connector = <->connector;520521if (lt->hdmi_port->ops & DRM_BRIDGE_OP_HPD) {522drm_bridge_hpd_enable(lt->hdmi_port, lt8912_bridge_hpd_cb, lt);523connector->polled = DRM_CONNECTOR_POLL_HPD;524} else {525connector->polled = DRM_CONNECTOR_POLL_CONNECT |526DRM_CONNECTOR_POLL_DISCONNECT;527}528529ret = drm_connector_init(bridge->dev, connector,530<8912_connector_funcs,531lt->hdmi_port->type);532if (ret)533goto exit;534535drm_connector_helper_add(connector, <8912_connector_helper_funcs);536537connector->dpms = DRM_MODE_DPMS_OFF;538drm_connector_attach_encoder(connector, bridge->encoder);539540exit:541return ret;542}543544static int lt8912_bridge_attach(struct drm_bridge *bridge,545struct drm_encoder *encoder,546enum drm_bridge_attach_flags flags)547{548struct lt8912 *lt = bridge_to_lt8912(bridge);549int ret;550551ret = drm_bridge_attach(encoder, lt->hdmi_port, bridge,552DRM_BRIDGE_ATTACH_NO_CONNECTOR);553if (ret < 0) {554dev_err(lt->dev, "Failed to attach next bridge (%d)\n", ret);555return ret;556}557558if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {559ret = lt8912_bridge_connector_init(bridge);560if (ret) {561dev_err(lt->dev, "Failed to init bridge ! (%d)\n", ret);562return ret;563}564}565566ret = lt8912_hard_power_on(lt);567if (ret)568return ret;569570ret = lt8912_soft_power_on(lt);571if (ret)572goto error;573574return 0;575576error:577lt8912_hard_power_off(lt);578return ret;579}580581static void lt8912_bridge_detach(struct drm_bridge *bridge)582{583struct lt8912 *lt = bridge_to_lt8912(bridge);584585lt8912_hard_power_off(lt);586587if (lt->connector.dev && lt->hdmi_port->ops & DRM_BRIDGE_OP_HPD)588drm_bridge_hpd_disable(lt->hdmi_port);589}590591static enum drm_mode_status592lt8912_bridge_mode_valid(struct drm_bridge *bridge,593const struct drm_display_info *info,594const struct drm_display_mode *mode)595{596if (mode->clock > 150000)597return MODE_CLOCK_HIGH;598599if (mode->hdisplay > 1920)600return MODE_BAD_HVALUE;601602if (mode->vdisplay > 1080)603return MODE_BAD_VVALUE;604605return MODE_OK;606}607608static enum drm_connector_status609lt8912_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector)610{611struct lt8912 *lt = bridge_to_lt8912(bridge);612613if (lt->hdmi_port->ops & DRM_BRIDGE_OP_DETECT)614return drm_bridge_detect(lt->hdmi_port, connector);615616return lt8912_check_cable_status(lt);617}618619static const struct drm_edid *lt8912_bridge_edid_read(struct drm_bridge *bridge,620struct drm_connector *connector)621{622struct lt8912 *lt = bridge_to_lt8912(bridge);623624/*625* edid must be read through the ddc bus but it must be626* given to the hdmi connector node.627*/628if (lt->hdmi_port->ops & DRM_BRIDGE_OP_EDID)629return drm_bridge_edid_read(lt->hdmi_port, connector);630631dev_warn(lt->dev, "The connected bridge does not supports DRM_BRIDGE_OP_EDID\n");632return NULL;633}634635static const struct drm_bridge_funcs lt8912_bridge_funcs = {636.attach = lt8912_bridge_attach,637.detach = lt8912_bridge_detach,638.mode_valid = lt8912_bridge_mode_valid,639.mode_set = lt8912_bridge_mode_set,640.enable = lt8912_bridge_enable,641.detect = lt8912_bridge_detect,642.edid_read = lt8912_bridge_edid_read,643};644645static int lt8912_bridge_resume(struct device *dev)646{647struct lt8912 *lt = dev_get_drvdata(dev);648int ret;649650ret = lt8912_hard_power_on(lt);651if (ret)652return ret;653654ret = lt8912_soft_power_on(lt);655if (ret)656return ret;657658return lt8912_video_on(lt);659}660661static int lt8912_bridge_suspend(struct device *dev)662{663struct lt8912 *lt = dev_get_drvdata(dev);664665lt8912_hard_power_off(lt);666667return 0;668}669670static DEFINE_SIMPLE_DEV_PM_OPS(lt8912_bridge_pm_ops, lt8912_bridge_suspend, lt8912_bridge_resume);671672static int lt8912_get_regulators(struct lt8912 *lt)673{674unsigned int i;675const char * const supply_names[] = {676"vdd", "vccmipirx", "vccsysclk", "vcclvdstx",677"vcchdmitx", "vcclvdspll", "vcchdmipll"678};679680for (i = 0; i < ARRAY_SIZE(lt->supplies); i++)681lt->supplies[i].supply = supply_names[i];682683return devm_regulator_bulk_get(lt->dev, ARRAY_SIZE(lt->supplies),684lt->supplies);685}686687static int lt8912_parse_dt(struct lt8912 *lt)688{689struct gpio_desc *gp_reset;690struct device *dev = lt->dev;691int ret;692int data_lanes;693struct device_node *port_node;694695gp_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);696if (IS_ERR(gp_reset)) {697ret = PTR_ERR(gp_reset);698if (ret != -EPROBE_DEFER)699dev_err(dev, "Failed to get reset gpio: %d\n", ret);700return ret;701}702lt->gp_reset = gp_reset;703704data_lanes = drm_of_get_data_lanes_count_ep(dev->of_node, 0, -1, 1, 4);705if (data_lanes < 0) {706dev_err(lt->dev, "%s: Bad data-lanes property\n", __func__);707return data_lanes;708}709710lt->data_lanes = data_lanes;711712lt->host_node = of_graph_get_remote_node(dev->of_node, 0, -1);713if (!lt->host_node) {714dev_err(lt->dev, "%s: Failed to get remote port\n", __func__);715return -ENODEV;716}717718port_node = of_graph_get_remote_node(dev->of_node, 1, -1);719if (!port_node) {720dev_err(lt->dev, "%s: Failed to get connector port\n", __func__);721ret = -ENODEV;722goto err_free_host_node;723}724725lt->hdmi_port = of_drm_find_bridge(port_node);726if (!lt->hdmi_port) {727ret = -EPROBE_DEFER;728dev_err_probe(lt->dev, ret, "%s: Failed to get hdmi port\n", __func__);729goto err_free_host_node;730}731732if (!of_device_is_compatible(port_node, "hdmi-connector")) {733dev_err(lt->dev, "%s: Failed to get hdmi port\n", __func__);734ret = -EINVAL;735goto err_free_host_node;736}737738ret = lt8912_get_regulators(lt);739if (ret)740goto err_free_host_node;741742of_node_put(port_node);743return 0;744745err_free_host_node:746of_node_put(port_node);747of_node_put(lt->host_node);748return ret;749}750751static int lt8912_put_dt(struct lt8912 *lt)752{753of_node_put(lt->host_node);754return 0;755}756757static int lt8912_probe(struct i2c_client *client)758{759static struct lt8912 *lt;760int ret = 0;761struct device *dev = &client->dev;762763lt = devm_drm_bridge_alloc(dev, struct lt8912, bridge,764<8912_bridge_funcs);765if (IS_ERR(lt))766return PTR_ERR(lt);767768lt->dev = dev;769lt->i2c_client[0] = client;770771ret = lt8912_parse_dt(lt);772if (ret)773goto err_dt_parse;774775ret = lt8912_init_i2c(lt, client);776if (ret)777goto err_i2c;778779i2c_set_clientdata(client, lt);780781lt->bridge.of_node = dev->of_node;782lt->bridge.ops = (DRM_BRIDGE_OP_EDID |783DRM_BRIDGE_OP_DETECT);784785drm_bridge_add(<->bridge);786787ret = lt8912_attach_dsi(lt);788if (ret)789goto err_attach;790791return 0;792793err_attach:794drm_bridge_remove(<->bridge);795lt8912_free_i2c(lt);796err_i2c:797lt8912_put_dt(lt);798err_dt_parse:799return ret;800}801802static void lt8912_remove(struct i2c_client *client)803{804struct lt8912 *lt = i2c_get_clientdata(client);805806drm_bridge_remove(<->bridge);807lt8912_free_i2c(lt);808lt8912_put_dt(lt);809}810811static const struct of_device_id lt8912_dt_match[] = {812{.compatible = "lontium,lt8912b"},813{}814};815MODULE_DEVICE_TABLE(of, lt8912_dt_match);816817static const struct i2c_device_id lt8912_id[] = {818{ "lt8912" },819{}820};821MODULE_DEVICE_TABLE(i2c, lt8912_id);822823static struct i2c_driver lt8912_i2c_driver = {824.driver = {825.name = "lt8912",826.of_match_table = lt8912_dt_match,827.pm = pm_sleep_ptr(<8912_bridge_pm_ops),828},829.probe = lt8912_probe,830.remove = lt8912_remove,831.id_table = lt8912_id,832};833module_i2c_driver(lt8912_i2c_driver);834835MODULE_AUTHOR("Adrien Grassein <[email protected]>");836MODULE_DESCRIPTION("lt8912 drm driver");837MODULE_LICENSE("GPL v2");838839840