diff options
author | Max Krummenacher <max.krummenacher@toradex.com> | 2019-08-02 11:31:26 +0200 |
---|---|---|
committer | Max Krummenacher <max.krummenacher@toradex.com> | 2019-08-07 18:25:51 +0200 |
commit | 732dc657e8de29b217875e0e3515f0b1b898b41b (patch) | |
tree | aa7adb40661504069f867f832851e84615c4ea58 | |
parent | 3a854d26bc61b99d50d7f46ef899c2c081b4002d (diff) |
lt8912: add gpio based hotplug detection
While at it remove commented out fragments.
While at it remove double execution of MIPI RX soft reset.
Signed-off-by: Max Krummenacher <max.krummenacher@toradex.com>
-rw-r--r-- | drivers/gpu/drm/bridge/lt8912.c | 274 |
1 files changed, 79 insertions, 195 deletions
diff --git a/drivers/gpu/drm/bridge/lt8912.c b/drivers/gpu/drm/bridge/lt8912.c index d994bd51585a..54480f7124fa 100644 --- a/drivers/gpu/drm/bridge/lt8912.c +++ b/drivers/gpu/drm/bridge/lt8912.c @@ -34,8 +34,10 @@ struct lt8912 { struct device_node *host_node; u8 num_dsi_lanes; u8 channel_id; + unsigned int irq; u8 sink_is_hdmi; struct regmap *regmap[3]; + struct gpio_desc *hpd_gpio; struct gpio_desc *reset_n; struct i2c_adapter *ddc; /* optional regular DDC I2C bus */ }; @@ -82,9 +84,6 @@ static void lt8912_init(struct lt8912 *lt) dev_info(lt->dev, "LT8912 ID: %02x, %02x\n", version[0], version[1]); -#if 1 -//settings from lontium(lt8912(3).c -//http://redmine.toradex.int/issues/40893 /* DigitalClockEn */ regmap_write(lt->regmap[0], 0x08, 0xff); regmap_write(lt->regmap[0], 0x09, 0xff); @@ -200,11 +199,6 @@ static void lt8912_init(struct lt8912 *lt) regmap_write(lt->regmap[1], 0x1e, 0x4f); regmap_write(lt->regmap[1], 0x51, 0x00); - /* mipi rx soft reset */ - regmap_write(lt->regmap[0], 0x03, 0x7f); - usleep_range(100000, 110000); - regmap_write(lt->regmap[0], 0x03, 0xff); - regmap_write(lt->regmap[0], 0xb2, lt->sink_is_hdmi); /* Audio Disable */ @@ -223,187 +217,52 @@ static void lt8912_init(struct lt8912 *lt) regmap_write(lt->regmap[1], 0x51, 0x80); usleep_range(10000, 20000); regmap_write(lt->regmap[1], 0x51, 0x00); -#else -//settings from rockchip -/* DigitalClockEn */ - regmap_write(lt->regmap[0], 0x08, 0xff); - regmap_write(lt->regmap[0], 0x09, 0x81); - regmap_write(lt->regmap[0], 0x0a, 0xff); - regmap_write(lt->regmap[0], 0x0b, 0x64); - regmap_write(lt->regmap[0], 0x0c, 0xff); - - regmap_write(lt->regmap[0], 0x44, 0x31); - regmap_write(lt->regmap[0], 0x51, 0x1f); - - /* TxAnalog */ - regmap_write(lt->regmap[0], 0x31, 0xa1); - regmap_write(lt->regmap[0], 0x32, 0xa1); - regmap_write(lt->regmap[0], 0x33, 0x03); - regmap_write(lt->regmap[0], 0x37, 0x00); - regmap_write(lt->regmap[0], 0x38, 0x22); - regmap_write(lt->regmap[0], 0x60, 0x82); - - /* CbusAnalog */ - regmap_write(lt->regmap[0], 0x39, 0x45); - regmap_write(lt->regmap[0], 0x3b, 0x00); - - /* HDMIPllAnalog */ - regmap_write(lt->regmap[0], 0x44, 0x31); - regmap_write(lt->regmap[0], 0x55, 0x44); - regmap_write(lt->regmap[0], 0x57, 0x01); - regmap_write(lt->regmap[0], 0x5a, 0x02); - - /* MipiBasicSet */ - regmap_write(lt->regmap[1], 0x10, 0x01); - regmap_write(lt->regmap[1], 0x11, 0x08); - regmap_write(lt->regmap[1], 0x12, 0x04); - regmap_write(lt->regmap[1], 0x13, lanes % 4); - regmap_write(lt->regmap[1], 0x14, 0x00); - - regmap_write(lt->regmap[1], 0x15, 0x00); - regmap_write(lt->regmap[1], 0x1a, 0x03); - regmap_write(lt->regmap[1], 0x1b, 0x03); - - /* MIPIDig */ - regmap_write(lt->regmap[1], 0x18, hsync); - regmap_write(lt->regmap[1], 0x19, vsync); - regmap_write(lt->regmap[1], 0x1c, hactive % 0x100); - regmap_write(lt->regmap[1], 0x1d, hactive >> 8); - - regmap_write(lt->regmap[1], 0x1e, 0x67); - regmap_write(lt->regmap[1], 0x2f, 0x0c); - - regmap_write(lt->regmap[1], 0x34, htotal % 0x100); - regmap_write(lt->regmap[1], 0x35, htotal >> 8); - regmap_write(lt->regmap[1], 0x36, vtotal % 0x100); - regmap_write(lt->regmap[1], 0x37, vtotal >> 8); - regmap_write(lt->regmap[1], 0x38, vbp % 0x100); - regmap_write(lt->regmap[1], 0x39, vbp >> 8); - regmap_write(lt->regmap[1], 0x3a, vfp % 0x100); - regmap_write(lt->regmap[1], 0x3b, vfp >> 8); - regmap_write(lt->regmap[1], 0x3c, hbp % 0x100); - regmap_write(lt->regmap[1], 0x3d, hbp >> 8); - regmap_write(lt->regmap[1], 0x3e, hfp % 0x100); - regmap_write(lt->regmap[1], 0x3f, hfp >> 8); - - /* DDSConfig */ - regmap_write(lt->regmap[1], 0x4e, 0x52); - regmap_write(lt->regmap[1], 0x4f, 0xde); - regmap_write(lt->regmap[1], 0x50, 0xc0); - regmap_write(lt->regmap[1], 0x51, 0x80); - regmap_write(lt->regmap[1], 0x51, 0x00); - - regmap_write(lt->regmap[1], 0x1f, 0x5e); - regmap_write(lt->regmap[1], 0x20, 0x01); - regmap_write(lt->regmap[1], 0x21, 0x2c); - regmap_write(lt->regmap[1], 0x22, 0x01); - regmap_write(lt->regmap[1], 0x23, 0xfa); - regmap_write(lt->regmap[1], 0x24, 0x00); - regmap_write(lt->regmap[1], 0x25, 0xc8); - regmap_write(lt->regmap[1], 0x26, 0x00); - regmap_write(lt->regmap[1], 0x27, 0x5e); - regmap_write(lt->regmap[1], 0x28, 0x01); - regmap_write(lt->regmap[1], 0x29, 0x2c); - regmap_write(lt->regmap[1], 0x2a, 0x01); - regmap_write(lt->regmap[1], 0x2b, 0xfa); - regmap_write(lt->regmap[1], 0x2c, 0x00); - regmap_write(lt->regmap[1], 0x2d, 0xc8); - regmap_write(lt->regmap[1], 0x2e, 0x00); - - regmap_write(lt->regmap[0], 0x03, 0x7f); - usleep_range(10000, 20000); - regmap_write(lt->regmap[0], 0x03, 0xff); - - regmap_write(lt->regmap[1], 0x42, 0x64); - regmap_write(lt->regmap[1], 0x43, 0x00); - regmap_write(lt->regmap[1], 0x44, 0x04); - regmap_write(lt->regmap[1], 0x45, 0x00); - regmap_write(lt->regmap[1], 0x46, 0x59); - regmap_write(lt->regmap[1], 0x47, 0x00); - regmap_write(lt->regmap[1], 0x48, 0xf2); - regmap_write(lt->regmap[1], 0x49, 0x06); - regmap_write(lt->regmap[1], 0x4a, 0x00); - regmap_write(lt->regmap[1], 0x4b, 0x72); - regmap_write(lt->regmap[1], 0x4c, 0x45); - regmap_write(lt->regmap[1], 0x4d, 0x00); - regmap_write(lt->regmap[1], 0x52, 0x08); - regmap_write(lt->regmap[1], 0x53, 0x00); - regmap_write(lt->regmap[1], 0x54, 0xb2); - regmap_write(lt->regmap[1], 0x55, 0x00); - regmap_write(lt->regmap[1], 0x56, 0xe4); - regmap_write(lt->regmap[1], 0x57, 0x0d); - regmap_write(lt->regmap[1], 0x58, 0x00); - regmap_write(lt->regmap[1], 0x59, 0xe4); - regmap_write(lt->regmap[1], 0x5a, 0x8a); - regmap_write(lt->regmap[1], 0x5b, 0x00); - regmap_write(lt->regmap[1], 0x5c, 0x34); - regmap_write(lt->regmap[1], 0x1e, 0x4f); - regmap_write(lt->regmap[1], 0x51, 0x00); - - regmap_write(lt->regmap[0], 0xb2, 0x01); - - /* AudioIIsEn */ - regmap_write(lt->regmap[2], 0x06, 0x08); - regmap_write(lt->regmap[2], 0x07, 0xf0); - - regmap_write(lt->regmap[2], 0x34, 0xd2); - - regmap_write(lt->regmap[2], 0x3c, 0x41); - - /* MIPIRxLogicRes */ - regmap_write(lt->regmap[0], 0x03, 0x7f); - usleep_range(10000, 20000); - regmap_write(lt->regmap[0], 0x03, 0xff); - - regmap_write(lt->regmap[1], 0x51, 0x80); - usleep_range(10000, 20000); - regmap_write(lt->regmap[1], 0x51, 0x00); -#endif } -static void lt8912_exit(struct lt8912 *lt) -{ - regmap_write(lt->regmap[0], 0x08, 0x00); - regmap_write(lt->regmap[0], 0x09, 0x81); - regmap_write(lt->regmap[0], 0x0a, 0x00); - regmap_write(lt->regmap[0], 0x0b, 0x20); - regmap_write(lt->regmap[0], 0x0c, 0x00); - - regmap_write(lt->regmap[0], 0x54, 0x1d); - regmap_write(lt->regmap[0], 0x51, 0x15); - - regmap_write(lt->regmap[0], 0x44, 0x31); - regmap_write(lt->regmap[0], 0x41, 0xbd); - regmap_write(lt->regmap[0], 0x5c, 0x11); - - regmap_write(lt->regmap[0], 0x30, 0x08); - regmap_write(lt->regmap[0], 0x31, 0x00); - regmap_write(lt->regmap[0], 0x32, 0x00); - regmap_write(lt->regmap[0], 0x33, 0x00); - regmap_write(lt->regmap[0], 0x34, 0x00); - regmap_write(lt->regmap[0], 0x35, 0x00); - regmap_write(lt->regmap[0], 0x36, 0x00); - regmap_write(lt->regmap[0], 0x37, 0x00); - regmap_write(lt->regmap[0], 0x38, 0x00); -} - -static void lt8912_power_on(struct lt8912 *lt) +static void lt8912_wakeup(struct lt8912 *lt) { gpiod_direction_output(lt->reset_n, 1); msleep(120); gpiod_direction_output(lt->reset_n, 0); + + regmap_write(lt->regmap[0], 0x08,0xff); /* enable clk gating */ + regmap_write(lt->regmap[0], 0x41,0x3c); /* MIPI Rx Power On */ + regmap_write(lt->regmap[0], 0x05,0xfb); /* DDS logical reset */ + regmap_write(lt->regmap[0], 0x05,0xff); + regmap_write(lt->regmap[0], 0x03,0x7f); /* MIPI RX logical reset */ + usleep_range(10000, 20000); + regmap_write(lt->regmap[0], 0x03,0xff); + regmap_write(lt->regmap[0], 0x32,0xa1); + regmap_write(lt->regmap[0], 0x33,0x03); } -static void lt8912_power_off(struct lt8912 *lt) +static void lt8912_sleep(struct lt8912 *lt) { + regmap_write(lt->regmap[0], 0x32,0xa0); + regmap_write(lt->regmap[0], 0x33,0x00); /* Disable HDMI output. */ + regmap_write(lt->regmap[0], 0x41,0x3d); /* MIPI Rx Power Down. */ + regmap_write(lt->regmap[0], 0x08,0x00); /* diable DDS clk. */ + gpiod_direction_output(lt->reset_n, 1); } static enum drm_connector_status lt8912_connector_detect(struct drm_connector *connector, bool force) { - /* TODO: HPD handing (reg[0xc1] - bit[7]) */ - return connector_status_connected; + struct lt8912 *lt = connector_to_lt8912(connector); + enum drm_connector_status hpd, hpd_last; + int timeout = 0; + + hpd = connector_status_unknown; + do { + hpd_last = hpd; + hpd = gpiod_get_value(lt->hpd_gpio) ? + connector_status_connected : connector_status_disconnected; + msleep(20); + timeout += 20; + } while((hpd_last != hpd) && (timeout < 500)); + + return hpd; } static const struct drm_connector_funcs lt8912_connector_funcs = { @@ -415,6 +274,16 @@ static const struct drm_connector_funcs lt8912_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; +static irqreturn_t lt8912_hpd_irq_thread(int irq, void *arg) +{ + struct lt8912 *lt = arg; + struct drm_connector *connector = <->connector; + + drm_helper_hpd_irq_event(connector->dev); + + return IRQ_HANDLED; +} + static struct drm_encoder * lt8912_connector_best_encoder(struct drm_connector *connector) { @@ -482,9 +351,6 @@ static int lt8912_connector_get_modes(struct drm_connector *connector) static enum drm_mode_status lt8912_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { -struct lt8912 *lt = connector_to_lt8912(connector); -dev_warn(lt->dev, "%s: Testing mode clk: %i, h: %i, v: %i\n", __func__, mode->clock, mode->hdisplay, mode->vdisplay); - if (mode->clock > 150000) return MODE_CLOCK_HIGH; @@ -506,29 +372,19 @@ static const struct drm_connector_helper_funcs lt8912_connector_helper_funcs = { static void lt8912_bridge_post_disable(struct drm_bridge *bridge) { struct lt8912 *lt = bridge_to_lt8912(bridge); - - lt8912_power_off(lt); -} - -static void lt8912_bridge_disable(struct drm_bridge *bridge) -{ - struct lt8912 *lt = bridge_to_lt8912(bridge); - - lt8912_exit(lt); + lt8912_sleep(lt); } static void lt8912_bridge_enable(struct drm_bridge *bridge) { struct lt8912 *lt = bridge_to_lt8912(bridge); - lt8912_init(lt); } static void lt8912_bridge_pre_enable(struct drm_bridge *bridge) { struct lt8912 *lt = bridge_to_lt8912(bridge); - - lt8912_power_on(lt); + lt8912_wakeup(lt); } static void lt8912_bridge_mode_set(struct drm_bridge *bridge, @@ -546,6 +402,8 @@ static int lt8912_bridge_attach(struct drm_bridge *bridge) struct drm_connector *connector = <->connector; int ret; + connector->polled = DRM_CONNECTOR_POLL_HPD; + ret = drm_connector_init(bridge->dev, connector, <8912_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); @@ -558,6 +416,9 @@ static int lt8912_bridge_attach(struct drm_bridge *bridge) drm_mode_connector_attach_encoder(connector, bridge->encoder); ret = lt8912_attach_dsi(lt); + + enable_irq(lt->irq); + return ret; } @@ -566,7 +427,6 @@ static const struct drm_bridge_funcs lt8912_bridge_funcs = { .mode_set = lt8912_bridge_mode_set, .pre_enable = lt8912_bridge_pre_enable, .enable = lt8912_bridge_enable, - .disable = lt8912_bridge_disable, .post_disable = lt8912_bridge_post_disable, }; @@ -639,9 +499,6 @@ int lt8912_attach_dsi(struct lt8912 *lt) dsi->lanes = lt->num_dsi_lanes; dsi->format = MIPI_DSI_FMT_RGB888; -/* adv7533 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | - MIPI_DSI_MODE_EOT_PACKET | MIPI_DSI_MODE_VIDEO_HSE; -*/ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET; @@ -672,6 +529,7 @@ static int lt8912_probe(struct i2c_client *i2c, const struct i2c_device_id *id) struct lt8912 *lt; struct device_node *ddc_phandle; struct device_node *endpoint; + unsigned int irq_flags; int ret; static int initialize_it = 1; @@ -696,6 +554,33 @@ static int lt8912_probe(struct i2c_client *i2c, const struct i2c_device_id *id) of_node_put(ddc_phandle); } + lt->hpd_gpio = devm_gpiod_get(dev, "hpd", GPIOD_IN); + if (IS_ERR(lt->hpd_gpio)) { + dev_err(dev, "failed to get hpd gpio\n"); + return ret; + } + + lt->irq = gpiod_to_irq(lt->hpd_gpio); + if (lt->irq == -ENXIO) { + dev_err(dev, "failed to get hpd irq\n"); + return -ENODEV; + } + if (lt->irq < 0) { + dev_err(dev, "failed to get hpd irq, %i\n", lt->irq); + return lt->irq; + } + irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(dev, lt->irq, + NULL, + lt8912_hpd_irq_thread, + irq_flags, "lt8912_hpd", lt); + if (ret) { + dev_err(dev, "failed to request irq\n"); + return -ENODEV; + } + + disable_irq(lt->irq); + lt->reset_n = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); if (IS_ERR(lt->reset_n)) { ret = PTR_ERR(lt->reset_n); @@ -725,8 +610,6 @@ static int lt8912_probe(struct i2c_client *i2c, const struct i2c_device_id *id) of_node_put(endpoint); of_node_put(lt->host_node); -// mipi_dsi_set_drvdata(lt->dsi, lt); - lt->bridge.funcs = <8912_bridge_funcs; lt->bridge.of_node = dev->of_node; ret = drm_bridge_add(<->bridge); @@ -742,6 +625,7 @@ static int lt8912_remove(struct i2c_client *i2c) { struct lt8912 *lt = i2c_get_clientdata(i2c); + lt8912_sleep(lt); mipi_dsi_detach(lt->dsi); drm_bridge_remove(<->bridge); |