novena-kernel-patches/0010-drm-bridge-it6251-add-...

812 lines
22 KiB
Diff

From d45e59bfbee9f13a261179dedd924422cd18f9b0 Mon Sep 17 00:00:00 2001
From: Sean Cross <xobs@kosagi.com>
Date: Wed, 30 Dec 2015 12:19:59 +0800
Subject: [PATCH 10/65] drm/bridge: it6251: add new lvds to dp converter
The IT6251 bridge chip can go from single-, dual-, or quad-lane LVDS to
DisplayPort and Mini DisplayPort.
Signed-off-by: Sean Cross <xobs@kosagi.com>
(sakaki: hunks 1 and 2 modified for 4.7.2)
---
drivers/gpu/drm/bridge/Kconfig | 10 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/it6251.c | 755 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 766 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/it6251.c
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 6dddd39..bcae9f5 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -32,4 +32,14 @@
the i.MX6 HDMI driver.
+config DRM_IT6251
+ tristate "IT6251 eDP/LVDS bridge"
+ depends on OF
+ select DRM_PANEL
+ select DRM_KMS_HELPER
+ select BACKLIGHT_LCD_SUPPORT
+ select BACKLIGHT_CLASS_DEVICE
+ ---help---
+ IT6251 eDP-LVDS bridge chip driver.
+
config DRM_NXP_PTN3460
tristate "NXP PTN3460 DP/LVDS bridge"
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index d4e28be..9b19c8f 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -4,4 +4,5 @@
obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
+obj-$(CONFIG_DRM_IT6251) += it6251.o
obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
diff --git a/drivers/gpu/drm/bridge/it6251.c b/drivers/gpu/drm/bridge/it6251.c
new file mode 100644
index 0000000..ef0cbd3
--- /dev/null
+++ b/drivers/gpu/drm/bridge/it6251.c
@@ -0,0 +1,755 @@
+/*
+ * Copyright (C) 2014 Sean Cross
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <drm/drm_panel.h>
+
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+#include "drm_atomic_helper.h"
+
+struct it6251_bridge {
+ struct drm_connector connector;
+ struct drm_bridge bridge;
+ struct device *dev;
+ struct i2c_client *client;
+ struct i2c_client *lvds_client;
+ struct regulator *regulator;
+ struct drm_panel *panel;
+};
+
+#define LVDS_ADDR 0x5e
+
+/* HW register definitions */
+
+#define IT6251_VENDOR_ID_LOW 0x00
+#define IT6251_VENDOR_ID_HIGH 0x01
+#define IT6251_DEVICE_ID_LOW 0x02
+#define IT6251_DEVICE_ID_HIGH 0x03
+#define IT6251_SYSTEM_STATUS 0x0d
+#define IT6251_SYSTEM_STATUS_RINTSTATUS (1 << 0)
+#define IT6251_SYSTEM_STATUS_RHPDSTATUS (1 << 1)
+#define IT6251_SYSTEM_STATUS_RVIDEOSTABLE (1 << 2)
+#define IT6251_SYSTEM_STATUS_RPLL_IOLOCK (1 << 3)
+#define IT6251_SYSTEM_STATUS_RPLL_XPLOCK (1 << 4)
+#define IT6251_SYSTEM_STATUS_RPLL_SPLOCK (1 << 5)
+#define IT6251_SYSTEM_STATUS_RAUXFREQ_LOCK (1 << 6)
+#define IT6251_REF_STATE 0x0e
+#define IT6251_REF_STATE_MAIN_LINK_DISABLED (1 << 0)
+#define IT6251_REF_STATE_AUX_CHANNEL_READ (1 << 1)
+#define IT6251_REF_STATE_CR_PATTERN (1 << 2)
+#define IT6251_REF_STATE_EQ_PATTERN (1 << 3)
+#define IT6251_REF_STATE_NORMAL_OPERATION (1 << 4)
+#define IT6251_REF_STATE_MUTED (1 << 5)
+#define IT6251_RPC_REQ 0x2b
+#define IT6251_RPC_REQ_RPC_FIFOFULL (1 << 6)
+#define IT6251_RPC_REQ_RPC_FIFOEMPTY (1 << 7)
+
+#define IT6251_REG_PCLK_CNT_LOW 0x57
+#define IT6251_REG_PCLK_CNT_HIGH 0x58
+
+#define IT6251_REG_LVDS_PORT_ADDR 0xfd
+#define IT6251_REG_LVDS_PORT_CTRL 0xfe
+#define IT6251_REG_LVDS_PORT_CTRL_EN (1 << 0)
+
+#define INIT_RETRY_DELAY_START msecs_to_jiffies(350)
+#define INIT_RETRY_DELAY_MAX msecs_to_jiffies(3000)
+#define INIT_RETRY_DELAY_INC msecs_to_jiffies(50)
+#define INIT_RETRY_MAX_TRIES 4
+
+#define it6251_lvds_write(it6251, addr, val) \
+ do { \
+ int ret; \
+ ret = _it6251_lvds_write(it6251, addr, val); \
+ if (ret) \
+ dev_err(&it6251->lvds_client->dev, "it6251.c:%s:%d error %d writing %d to %d\n", __func__, __LINE__, ret, val, addr); \
+ } while(0)
+
+#define it6251_write(it6251, addr, val) \
+ do { \
+ int ret; \
+ ret = _it6251_write(it6251, addr, val); \
+ if (ret) \
+ dev_err(&it6251->lvds_client->dev, "it6251.c:%s:%d error %d writing %d to %d\n", __func__, __LINE__, ret, val, addr); \
+ } while(0)
+
+static inline struct it6251_bridge *
+ bridge_to_it6251(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct it6251_bridge, bridge);
+}
+
+static inline struct it6251_bridge *
+ connector_to_it6251(struct drm_connector *connector)
+{
+ return container_of(connector, struct it6251_bridge, connector);
+}
+
+/* HW access functions */
+
+static int
+_it6251_write(struct it6251_bridge *it6251, uint8_t addr, uint8_t val)
+{
+ struct i2c_client *client = it6251->client;
+ uint8_t buf[2] = {addr, val};
+ int ret;
+
+ ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
+ if (ret < 0) {
+ dev_err(&client->dev, "error %d writing to edp addr 0x%x\n",
+ ret, addr);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+_it6251_lvds_write(struct it6251_bridge *it6251, uint8_t addr, uint8_t val)
+{
+ struct i2c_client *client = it6251->lvds_client;
+ uint8_t buf[2] = {addr, val};
+ int ret;
+
+ ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
+ if (ret < 0) {
+ dev_err(&client->dev, "error %d writing to lvds addr 0x%x\n",
+ ret, addr);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+it6251_read(struct it6251_bridge *it6251, uint8_t addr)
+{
+ struct i2c_client *client = it6251->client;
+ uint8_t val;
+ int ret;
+
+ ret = i2c_master_send(client, &addr, sizeof(addr));
+ if (ret < 0)
+ goto fail;
+
+ ret = i2c_master_recv(client, (void *)&val, sizeof(val));
+ if (ret < 0)
+ goto fail;
+
+ return val;
+
+fail:
+ dev_err(&client->dev, "Error %d reading from subaddress 0x%x\n",
+ ret, addr);
+ return -1;
+}
+
+static int
+it6251_lvds_read(struct it6251_bridge *it6251, uint8_t addr)
+{
+ struct i2c_client *client = it6251->lvds_client;
+ uint8_t val;
+ int ret;
+
+ ret = i2c_master_send(client, &addr, sizeof(addr));
+ if (ret < 0)
+ goto fail;
+
+ ret = i2c_master_recv(client, (void *)&val, sizeof(val));
+ if (ret < 0)
+ goto fail;
+
+ return val;
+
+fail:
+ dev_err(&client->dev, "Error %d reading from subaddress 0x%x\n",
+ ret, addr);
+ return -1;
+}
+
+static int it6251_is_stable(struct it6251_bridge *it6251)
+{
+ int status;
+ int rpclkcnt;
+ int clkcnt;
+ int refstate;
+ int rpcreq;
+ u16 hactive;
+ u16 vactive;
+
+ status = it6251_read(it6251, IT6251_SYSTEM_STATUS);
+ dev_info(it6251->dev, "System status: 0x%02x\n", status);
+
+ if (!(status & IT6251_SYSTEM_STATUS_RVIDEOSTABLE))
+ return 0;
+
+ rpclkcnt = ((it6251_read(it6251, 0x13) & 0xff)
+ | ((it6251_read(it6251, 0x14) << 8) & 0x0f00));
+ dev_info(it6251->dev, "RPCLKCnt: %d\n", rpclkcnt);
+
+ clkcnt = ((it6251_lvds_read(it6251, IT6251_REG_PCLK_CNT_LOW) & 0xff) |
+ ((it6251_lvds_read(it6251, IT6251_REG_PCLK_CNT_HIGH) << 8) & 0x0f00));
+ dev_info(it6251->dev, "Clock: 0x%02x\n", clkcnt);
+
+ refstate = it6251_lvds_read(it6251, IT6251_REF_STATE);
+ dev_info(it6251->dev, "Ref Link State: 0x%02x\n", refstate);
+
+ rpcreq = it6251_lvds_read(it6251, IT6251_RPC_REQ);
+ dev_info(it6251->dev, "RPC Req: 0x%02x\n", rpcreq);
+
+ hactive = it6251_read(it6251, 0xa5);
+ hactive = ((it6251_read(it6251, 0xa6) & 0x1f) << 8) + hactive;
+ dev_info(it6251->dev, "hactive: %d\n", hactive);
+
+ vactive = it6251_read(it6251, 0xaf);
+ vactive = ((it6251_read(it6251, 0xb0) & 0x0f) << 8) + vactive;
+ dev_info(it6251->dev, "vactive: %d\n", vactive);
+
+ if ((refstate & 0x1f) != 0)
+ return 0;
+
+ if (rpcreq & IT6251_RPC_REQ_RPC_FIFOFULL) {
+ dev_err(it6251->dev, "RPC fifofull is set, might be an error\n");
+ return 0;
+ }
+
+ /* If video is muted, that's a failure */
+ if (refstate & IT6251_REF_STATE_MUTED)
+ return 0;
+
+ if (it6251->panel) {
+ struct drm_panel *panel = it6251->panel;
+
+ if (panel->connector) {
+ struct drm_display_mode *mode;
+
+ list_for_each_entry(mode, &panel->connector->modes, head) {
+ if ((mode->hdisplay == hactive)
+ && (mode->vdisplay == vactive))
+ return 1;
+ }
+ }
+ else
+ dev_info(it6251->dev, "no panel connector\n");
+ }
+ else
+ dev_info(it6251->dev, "no panel\n");
+
+ dev_info(it6251->dev, "no match found\n");
+
+ if (vactive == 1080)
+ return 1;
+
+ return 0;
+}
+
+static int it6251_init(struct it6251_bridge *it6251)
+{
+ int reg;
+ int stable_delays;
+
+ /* The bootloader can leave the chip already initialized */
+ if (it6251_is_stable(it6251)) {
+ dev_info(it6251->dev, "eDP system is already stable\n");
+ return 0;
+ }
+
+ /* Reset DisplayPort half (setting bit 2 causes it to not respond
+ * over i2c, which is considered "normal". This write will report
+ * failure, but will actually succeed. */
+ it6251_write(it6251, 0x05, 0xff);
+ it6251_write(it6251, 0x05, 0x00);
+
+ /* Configure LVDS receiver */
+ it6251_write(it6251, IT6251_REG_LVDS_PORT_ADDR,
+ LVDS_ADDR << 1);
+ it6251_write(it6251, IT6251_REG_LVDS_PORT_CTRL,
+ IT6251_REG_LVDS_PORT_CTRL_EN);
+
+ // LVDSRX
+ it6251_lvds_write(it6251, 0x05, 0xff); // reset LVDSRX
+ it6251_lvds_write(it6251, 0x05, 0x00);
+
+ it6251_lvds_write(it6251, 0x3b, 0x42); // reset LVDSRX PLL
+ it6251_lvds_write(it6251, 0x3b, 0x43);
+
+ it6251_lvds_write(it6251, 0x3c, 0x08); // something with SSC PLL
+ it6251_lvds_write(it6251, 0x0b, 0x88); // don't swap links, but writing reserved registers
+
+ it6251_lvds_write(it6251, 0x2c, 0x01); // JEIDA, 8-bit depth 0x11 // orig 0x42
+ it6251_lvds_write(it6251, 0x32, 0x04); // "reserved"
+ it6251_lvds_write(it6251, 0x35, 0xe0); // "reserved"
+ it6251_lvds_write(it6251, 0x2b, 0x24); // "reserved" + clock delay
+
+ it6251_lvds_write(it6251, 0x05, 0x02); // reset LVDSRX pix clock
+ it6251_lvds_write(it6251, 0x05, 0x00);
+
+ // DPTX
+ it6251_write(it6251, 0x16, 0x02); // set for two lane mode, normal op, no swapping, no downspread
+
+ it6251_write(it6251, 0x23, 0x40); // some AUX channel EDID magic
+
+ it6251_write(it6251, 0x5c, 0xf3); // power down lanes 3-0
+
+ it6251_write(it6251, 0x5f, 0x06); // enable DP scrambling, change EQ CR phase
+
+ it6251_write(it6251, 0x60, 0x02); // color mode RGB, pclk/2
+ it6251_write(it6251, 0x61, 0x04); // dual pixel input mode, no EO swap, no RGB swap
+ it6251_write(it6251, 0x62, 0x01); // M444B24 video format
+
+ // vesa range / not interlace / vsync high / hsync high
+ // 00001111
+ it6251_write(it6251, 0xa0, 0x0F);
+
+ it6251_write(it6251, 0xc9, 0xf5); // hpd event timer set to 1.6-ish ms
+
+ it6251_write(it6251, 0xca, 0x4d); // more reserved magic
+ it6251_write(it6251, 0xcb, 0x37);
+
+ it6251_write(it6251, 0xd3, 0x03); // enhanced framing mode, auto video fifo reset, video mute disable
+
+ it6251_write(it6251, 0xd4, 0x45); // "vidstmp" and some reserved stuff
+
+ it6251_write(it6251, 0xe7, 0xa0); // queue number -- reserved
+ it6251_write(it6251, 0xe8, 0x33); // info frame packets and reserved
+ it6251_write(it6251, 0xec, 0x00); // more AVI stuff
+
+ it6251_write(it6251, 0x23, 0x42); // select PC master reg for aux channel?
+
+ it6251_write(it6251, 0x24, 0x00); // send PC request commands
+ it6251_write(it6251, 0x25, 0x00);
+ it6251_write(it6251, 0x26, 0x00);
+
+ it6251_write(it6251, 0x2b, 0x00); // native aux read
+ it6251_write(it6251, 0x23, 0x40); // back to internal
+
+ it6251_write(it6251, 0x19, 0xff); // voltage swing level 3
+ it6251_write(it6251, 0x1a, 0xff); // pre-emphasis level 3
+
+ it6251_write(it6251, 0x17, 0x01); // start link training
+
+ for (stable_delays = 0; stable_delays < 100; stable_delays++) {
+ reg = it6251_read(it6251, 0x0e);
+ if ((reg & 0x1f) == 0x10) {
+ reg = it6251_read(it6251, IT6251_SYSTEM_STATUS);
+ if (reg & IT6251_SYSTEM_STATUS_RVIDEOSTABLE)
+ break;
+ }
+ udelay(2000);
+ }
+
+ /*
+ * If we couldn't stabilize, requeue and try again, because it means
+ * that the LVDS channel isn't stable yet.
+ */
+ if (!it6251_is_stable(it6251)) {
+ dev_err(it6251->dev, "warning: bridge is not stable\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int it6251_power_up(struct it6251_bridge *it6251)
+{
+ struct i2c_client *client = it6251->client;
+ int vendor_id_lo, vendor_id_hi, device_id_lo, device_id_hi;
+ int ret;
+ int i;
+ int max_tries = 5;
+
+ ret = regulator_enable(it6251->regulator);
+ if (ret) {
+ dev_err(&client->dev, "Unable to enable regulator\n");
+ return ret;
+ }
+
+ /* Sometimes it seems like multiple tries are needed */
+ for (i = 0; i < max_tries; i++) {
+ vendor_id_lo = it6251_read(it6251, IT6251_VENDOR_ID_LOW);
+ vendor_id_hi = it6251_read(it6251, IT6251_VENDOR_ID_HIGH);
+ device_id_lo = it6251_read(it6251, IT6251_DEVICE_ID_LOW);
+ device_id_hi = it6251_read(it6251, IT6251_DEVICE_ID_HIGH);
+
+ if ((vendor_id_lo != -1)
+ && (vendor_id_hi != -1)
+ && (device_id_lo != -1)
+ && (device_id_hi != -1))
+ break;
+ usleep_range(100000, 200000);
+ }
+
+ if ((vendor_id_lo == -1) || (vendor_id_hi == -1)
+ || (device_id_lo == -1) || (device_id_hi == -1)) {
+
+ dev_err(&client->dev, "unable to read product id, deferring\n");
+
+ ret = regulator_disable(it6251->regulator);
+ if (ret)
+ dev_err(&client->dev, "unable to disable regulator\n");
+
+ return -EPROBE_DEFER;
+ }
+
+ return 0;
+}
+
+static int it6251_suspend(struct device *dev, bool do_power_down)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct it6251_bridge *it6251 = i2c_get_clientdata(client);
+
+ if (do_power_down && regulator_is_enabled(it6251->regulator)) {
+ int ret;
+
+ ret = regulator_disable(it6251->regulator);
+ if (ret) {
+ dev_err(&client->dev, "unable to disable regulator\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int it6251_resume(struct device *dev, bool do_power_up)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct it6251_bridge *it6251 = i2c_get_clientdata(client);
+
+ it6251 = i2c_get_clientdata(client);
+
+ if (do_power_up) {
+ int ret;
+
+ ret = it6251_power_up(it6251);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* I2C driver functions */
+
+static int it6251_pm_suspend(struct device *dev)
+{
+ return it6251_suspend(dev, true);
+}
+
+static int it6251_pm_resume(struct device *dev)
+{
+ return it6251_resume(dev, true);
+}
+
+static int it6251_pm_freeze(struct device *dev)
+{
+ return it6251_suspend(dev, false);
+}
+
+static int it6251_pm_thaw(struct device *dev)
+{
+ return it6251_resume(dev, false);
+}
+
+static void it6251_pre_enable(struct drm_bridge *bridge)
+{
+ struct it6251_bridge *it6251 = bridge_to_it6251(bridge);
+ int ret;
+
+ if (drm_panel_prepare(it6251->panel)) {
+ DRM_ERROR("failed to prepare panel\n");
+ return;
+ }
+
+ ret = it6251_power_up(it6251);
+ if (ret)
+ dev_err(it6251->dev, "unable to power up chip\n");
+}
+
+static void it6251_enable(struct drm_bridge *bridge)
+{
+ struct it6251_bridge *it6251 = bridge_to_it6251(bridge);
+ int tries;
+
+ if (drm_panel_enable(it6251->panel)) {
+ DRM_ERROR("failed to enable panel\n");
+ return;
+ }
+
+ for (tries = 0; tries < 5; tries++) {
+ int ret;
+
+ ret = it6251_init(it6251);
+ if (!ret)
+ break;
+
+ /* If the resolution isn't correct, restart the chip */
+ ret = regulator_disable(it6251->regulator);
+ if (ret)
+ dev_err(it6251->dev, "unable to disable regulator\n");
+
+ ret = it6251_power_up(it6251);
+ if (ret)
+ dev_err(it6251->dev, "unable to power up\n");
+ }
+}
+
+static void it6251_disable(struct drm_bridge *bridge)
+{
+ struct it6251_bridge *it6251 = bridge_to_it6251(bridge);
+
+ if (drm_panel_disable(it6251->panel)) {
+ DRM_ERROR("failed to disable panel\n");
+ return;
+ }
+}
+
+static void it6251_post_disable(struct drm_bridge *bridge)
+{
+ struct it6251_bridge *it6251 = bridge_to_it6251(bridge);
+
+ if (drm_panel_unprepare(it6251->panel)) {
+ DRM_ERROR("failed to unprepare panel\n");
+ return;
+ }
+}
+
+static int it6251_get_modes(struct drm_connector *connector)
+{
+ struct it6251_bridge *it6251;
+
+ it6251 = connector_to_it6251(connector);
+
+ return drm_panel_get_modes(it6251->panel);
+}
+
+static struct drm_encoder *it6251_best_encoder(struct drm_connector *connector)
+{
+ struct it6251_bridge *it6251;
+
+ it6251 = connector_to_it6251(connector);
+
+ return it6251->bridge.encoder;
+}
+
+static const struct drm_connector_helper_funcs it6251_connector_helper_funcs = {
+ .get_modes = it6251_get_modes,
+ .best_encoder = it6251_best_encoder,
+};
+
+static enum drm_connector_status it6251_detect(struct drm_connector *connector,
+ bool force)
+{
+ return connector_status_connected;
+}
+
+static void it6251_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs it6251_connector_funcs = {
+ .dpms = drm_atomic_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = it6251_detect,
+ .destroy = it6251_connector_destroy,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int it6251_attach(struct drm_bridge *bridge)
+{
+ struct it6251_bridge *it6251 = bridge_to_it6251(bridge);
+ int ret;
+
+ if (!bridge->encoder) {
+ DRM_ERROR("Parent encoder object not found");
+ return -ENODEV;
+ }
+
+ it6251->connector.polled = DRM_CONNECTOR_POLL_HPD;
+ ret = drm_connector_init(bridge->dev, &it6251->connector,
+ &it6251_connector_funcs, DRM_MODE_CONNECTOR_eDP);
+ if (ret) {
+ DRM_ERROR("Failed to initialize connector with drm\n");
+ return ret;
+ }
+ drm_atomic_helper_connector_reset(&it6251->connector);
+ drm_connector_helper_add(&it6251->connector,
+ &it6251_connector_helper_funcs);
+ drm_mode_connector_attach_encoder(&it6251->connector, bridge->encoder);
+
+ if (it6251->panel)
+ drm_panel_attach(it6251->panel, &it6251->connector);
+ else
+ dev_err(it6251->dev, "no panel found for attach\n");
+
+ drm_helper_hpd_irq_event(it6251->connector.dev);
+
+ return 0;
+}
+
+static const struct drm_bridge_funcs it6251_bridge_funcs = {
+ .pre_enable = it6251_pre_enable,
+ .enable = it6251_enable,
+ .disable = it6251_disable,
+ .post_disable = it6251_post_disable,
+ .attach = it6251_attach,
+};
+
+static int
+it6251_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct it6251_bridge *it6251;
+ struct device_node *endpoint, *panel_node;
+ int ret;
+
+ it6251 = devm_kzalloc(&client->dev, sizeof(*it6251), GFP_KERNEL);
+ if (!it6251) {
+ dev_err(&client->dev, "unable to allocate it6251 data\n");
+ return -ENOMEM;
+ }
+
+ it6251->dev = dev;
+
+ endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+ if (endpoint) {
+ panel_node = of_graph_get_remote_port_parent(endpoint);
+ if (panel_node) {
+ it6251->panel = of_drm_find_panel(panel_node);
+ of_node_put(panel_node);
+ if (!it6251->panel) {
+ dev_warn(&client->dev,
+ "Unable to find panel, deferring\n");
+ return -EPROBE_DEFER;
+ }
+ }
+ }
+
+ it6251->regulator = devm_regulator_get(&client->dev, "power");
+ if (IS_ERR(it6251->regulator)) {
+ dev_err(&client->dev, "Unable to get regulator\n");
+ ret = PTR_ERR(it6251->regulator);
+ it6251->regulator = NULL;
+ goto err;
+ }
+
+ it6251->client = client;
+ i2c_set_clientdata(client, it6251);
+
+ it6251->bridge.funcs = &it6251_bridge_funcs;
+ it6251->bridge.of_node = dev->of_node;
+ ret = drm_bridge_add(&it6251->bridge);
+ if (ret) {
+ DRM_ERROR("Failed to add bridge\n");
+ return ret;
+ }
+
+ /* The LVDS-half of the chip shows up at address 0x5e */
+ it6251->lvds_client = i2c_new_dummy(it6251->client->adapter, LVDS_ADDR);
+ if (!it6251->lvds_client) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ dev_err(&client->dev, "Succeeded in probing it6251\n");
+ return 0;
+
+err:
+ if (it6251->lvds_client)
+ i2c_unregister_device(it6251->lvds_client);
+
+ dev_err(&client->dev, "Returning error: %d\n", ret);
+ return ret;
+}
+
+static int it6251_remove(struct i2c_client *client)
+{
+ struct it6251_bridge *it6251 = i2c_get_clientdata(client);
+ int ret;
+
+ if (it6251->lvds_client)
+ i2c_unregister_device(it6251->lvds_client);
+
+ ret = regulator_disable(it6251->regulator);
+ if (ret) {
+ dev_err(&client->dev, "unable to enable regulator\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops it6251_dev_pm_ops = {
+ .suspend = it6251_pm_suspend,
+ .resume = it6251_pm_resume,
+ .freeze = it6251_pm_freeze,
+ .thaw = it6251_pm_thaw,
+ .poweroff = it6251_pm_freeze,
+ .restore = it6251_pm_resume,
+};
+
+static struct i2c_device_id it6251_ids[] = {
+ { "it6251", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, it6251_ids);
+
+static const struct of_device_id it6251_of_match[] = {
+ { .compatible = "it,it6251", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, it6251_of_match);
+
+static struct i2c_driver it6251_driver = {
+ .driver = {
+ .name = "it6251",
+ .owner = THIS_MODULE,
+ .pm = &it6251_dev_pm_ops,
+ .of_match_table = it6251_of_match,
+ },
+ .probe = it6251_probe,
+ .remove = it6251_remove,
+ .id_table = it6251_ids,
+};
+
+module_i2c_driver(it6251_driver);
+
+/* Module initialization */
+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
+MODULE_DESCRIPTION("ITE Tech 6251 LVDS to DisplayPort encoder");
+MODULE_LICENSE("GPL");
--
2.7.3