summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/bridge
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2020-08-06 05:50:06 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2020-08-06 05:50:06 +0300
commit8186749621ed6b8fc42644c399e8c755a2b6f630 (patch)
tree3a1db67415da013e5dd481367c77db21e491edfb /drivers/gpu/drm/bridge
parente4a7b2dc35d9582c253cf5e6d6c3605aabc7284d (diff)
parentdc100bc8fae59aafd2ea2e1a1a43ef1f65f8a8bc (diff)
downloadlinux-8186749621ed6b8fc42644c399e8c755a2b6f630.tar.xz
Merge tag 'drm-next-2020-08-06' of git://anongit.freedesktop.org/drm/drm
Pull drm updates from Dave Airlie: "New xilinx displayport driver, AMD support for two new GPUs (more header files), i915 initial support for RocketLake and some work on their DG1 (discrete chip). The core also grew some lockdep annotations to try and constrain what drivers do with dma-fences, and added some documentation on why the idea of indefinite fences doesn't work. The long list is below. I do have some fixes trees outstanding, but I'll follow up with those later. core: - add user def flag to cmd line modes - dma_fence_wait added might_sleep - dma-fence lockdep annotations - indefinite fences are bad documentation - gem CMA functions used in more drivers - struct mutex removal - more drm_ debug macro usage - set/drop master api fixes - fix for drm/mm hole size comparison - drm/mm remove invalid entry optimization - optimise drm/mm hole handling - VRR debugfs added - uncompressed AFBC modifier support - multiple display id blocks in EDID - multiple driver sg handling fixes - __drm_atomic_helper_crtc_reset in all drivers - managed vram helpers ttm: - ttm_mem_reg handling cleanup - remove bo offset field - drop CMA memtype flag - drop mappable flag xilinx: - New Xilinx ZynqMP DisplayPort Subsystem driver nouveau: - add CRC support - start using NVIDIA published class header files - convert all push buffer emission to new macros - Proper push buffer space management for EVO/NVD channels. - firmware loading fixes - 2MiB system memory pages support on Pascal and newer vkms: - larger cursor support i915: - Rocketlake platform enablement - Early DG1 enablement - Numerous GEM refactorings - DP MST fixes - FBC, PSR, Cursor, Color, Gamma fixes - TGL, RKL, EHL workaround updates - TGL 8K display support fixes - SDVO/HDMI/DVI fixes amdgpu: - Initial support for Sienna Cichlid GPU - Initial support for Navy Flounder GPU - SI UVD/VCE support - expose rotation property - Add support for unique id on Arcturus - Enable runtime PM on vega10 boards that support BACO - Skip BAR resizing if the bios already did id - Major swSMU code cleanup - Fixes for DCN bandwidth calculations amdkfd: - Track SDMA usage per process - SMI events interface radeon: - Default to on chip GART for AGP boards on all arches - Runtime PM reference count fixes msm: - headers regenerated causing churn - a650/a640 display and GPU enablement - dpu dither support for 6bpc panels - dpu cursor fix - dsi/mdp5 enablement for sdm630/sdm636/sdm66 tegra: - video capture prep support - reflection support mediatek: - convert mtk_dsi to bridge API meson: - FBC support sun4i: - iommu support rockchip: - register locking fix - per-pixel alpha support PX30 VOP mgag200: - ported to simple and shmem helpers - device init cleanups - use managed pci functions - dropped hw cursor support ast: - use managed pci functions - use managed VRAM helpers - rework cursor support malidp: - dev_groups support hibmc: - refactor hibmc_drv_vdac: vc4: - create TXP CRTC imx: - error path fixes and cleanups etnaviv: - clock handling and error handling cleanups - use pin_user_pages" * tag 'drm-next-2020-08-06' of git://anongit.freedesktop.org/drm/drm: (1747 commits) drm/msm: use kthread_create_worker instead of kthread_run drm/msm/mdp5: Add MDP5 configuration for SDM636/660 drm/msm/dsi: Add DSI configuration for SDM660 drm/msm/mdp5: Add MDP5 configuration for SDM630 drm/msm/dsi: Add phy configuration for SDM630/636/660 drm/msm/a6xx: add A640/A650 hwcg drm/msm/a6xx: hwcg tables in gpulist drm/msm/dpu: add SM8250 to hw catalog drm/msm/dpu: add SM8150 to hw catalog drm/msm/dpu: intf timing path for displayport drm/msm/dpu: set missing flush bits for INTF_2 and INTF_3 drm/msm/dpu: don't use INTF_INPUT_CTRL feature on sdm845 drm/msm/dpu: move some sspp caps to dpu_caps drm/msm/dpu: update UBWC config for sm8150 and sm8250 drm/msm/dpu: use right setup_blend_config for sm8150 and sm8250 drm/msm/a6xx: set ubwc config for A640 and A650 drm/msm/adreno: un-open-code some packets drm/msm: sync generated headers drm/msm/a6xx: add build_bw_table for A640/A650 drm/msm/a6xx: fix crashstate capture for A650 ...
Diffstat (limited to 'drivers/gpu/drm/bridge')
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7511_drv.c137
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix-anx6345.c1
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c1
-rw-r--r--drivers/gpu/drm/bridge/cdns-dsi.c1
-rw-r--r--drivers/gpu/drm/bridge/chrontel-ch7033.c1
-rw-r--r--drivers/gpu/drm/bridge/nwl-dsi.c1
-rw-r--r--drivers/gpu/drm/bridge/sii902x.c2
-rw-r--r--drivers/gpu/drm/bridge/sii9234.c1
-rw-r--r--drivers/gpu/drm/bridge/sil-sii8620.c3
-rw-r--r--drivers/gpu/drm/bridge/simple-bridge.c113
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.c356
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c2
-rw-r--r--drivers/gpu/drm/bridge/tc358767.c1
-rw-r--r--drivers/gpu/drm/bridge/tc358768.c3
-rw-r--r--drivers/gpu/drm/bridge/thc63lvd1024.c1
-rw-r--r--drivers/gpu/drm/bridge/ti-sn65dsi86.c327
-rw-r--r--drivers/gpu/drm/bridge/ti-tfp410.c21
17 files changed, 685 insertions, 287 deletions
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
index 648eb23d0784..a0d392c338da 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
@@ -443,9 +443,14 @@ static void adv7511_hpd_work(struct work_struct *work)
if (adv7511->connector.status != status) {
adv7511->connector.status = status;
- if (status == connector_status_disconnected)
- cec_phys_addr_invalidate(adv7511->cec_adap);
- drm_kms_helper_hotplug_event(adv7511->connector.dev);
+
+ if (adv7511->connector.dev) {
+ if (status == connector_status_disconnected)
+ cec_phys_addr_invalidate(adv7511->cec_adap);
+ drm_kms_helper_hotplug_event(adv7511->connector.dev);
+ } else {
+ drm_bridge_hpd_notify(&adv7511->bridge, status);
+ }
}
}
@@ -589,11 +594,10 @@ static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block,
* ADV75xx helpers
*/
-static int adv7511_get_modes(struct adv7511 *adv7511,
- struct drm_connector *connector)
+static struct edid *adv7511_get_edid(struct adv7511 *adv7511,
+ struct drm_connector *connector)
{
struct edid *edid;
- unsigned int count;
/* Reading the EDID only works if the device is powered */
if (!adv7511->powered) {
@@ -612,15 +616,25 @@ static int adv7511_get_modes(struct adv7511 *adv7511,
if (!adv7511->powered)
__adv7511_power_off(adv7511);
-
- drm_connector_update_edid_property(connector, edid);
- count = drm_add_edid_modes(connector, edid);
-
adv7511_set_config_csc(adv7511, connector, adv7511->rgb,
drm_detect_hdmi_monitor(edid));
cec_s_phys_addr_from_edid(adv7511->cec_adap, edid);
+ return edid;
+}
+
+static int adv7511_get_modes(struct adv7511 *adv7511,
+ struct drm_connector *connector)
+{
+ struct edid *edid;
+ unsigned int count;
+
+ edid = adv7511_get_edid(adv7511, connector);
+
+ drm_connector_update_edid_property(connector, edid);
+ count = drm_add_edid_modes(connector, edid);
+
kfree(edid);
return count;
@@ -652,7 +666,8 @@ adv7511_detect(struct adv7511 *adv7511, struct drm_connector *connector)
if (status == connector_status_connected && hpd && adv7511->powered) {
regcache_mark_dirty(adv7511->regmap);
adv7511_power_on(adv7511);
- adv7511_get_modes(adv7511, connector);
+ if (connector)
+ adv7511_get_modes(adv7511, connector);
if (adv7511->status == connector_status_connected)
status = connector_status_disconnected;
} else {
@@ -774,7 +789,10 @@ static void adv7511_mode_set(struct adv7511 *adv7511,
adv7511->f_tmds = mode->clock;
}
-/* Connector funcs */
+/* -----------------------------------------------------------------------------
+ * DRM Connector Operations
+ */
+
static struct adv7511 *connector_to_adv7511(struct drm_connector *connector)
{
return container_of(connector, struct adv7511, connector);
@@ -818,7 +836,40 @@ static const struct drm_connector_funcs adv7511_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
-/* Bridge funcs */
+static int adv7511_connector_init(struct adv7511 *adv)
+{
+ struct drm_bridge *bridge = &adv->bridge;
+ int ret;
+
+ if (!bridge->encoder) {
+ DRM_ERROR("Parent encoder object not found");
+ return -ENODEV;
+ }
+
+ if (adv->i2c_main->irq)
+ adv->connector.polled = DRM_CONNECTOR_POLL_HPD;
+ else
+ adv->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
+ DRM_CONNECTOR_POLL_DISCONNECT;
+
+ ret = drm_connector_init(bridge->dev, &adv->connector,
+ &adv7511_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA);
+ if (ret < 0) {
+ DRM_ERROR("Failed to initialize connector with drm\n");
+ return ret;
+ }
+ drm_connector_helper_add(&adv->connector,
+ &adv7511_connector_helper_funcs);
+ drm_connector_attach_encoder(&adv->connector, bridge->encoder);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DRM Bridge Operations
+ */
+
static struct adv7511 *bridge_to_adv7511(struct drm_bridge *bridge)
{
return container_of(bridge, struct adv7511, bridge);
@@ -851,34 +902,13 @@ static int adv7511_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct adv7511 *adv = bridge_to_adv7511(bridge);
- int ret;
-
- if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
- DRM_ERROR("Fix bridge driver to make connector optional!");
- return -EINVAL;
- }
-
- if (!bridge->encoder) {
- DRM_ERROR("Parent encoder object not found");
- return -ENODEV;
- }
-
- if (adv->i2c_main->irq)
- adv->connector.polled = DRM_CONNECTOR_POLL_HPD;
- else
- adv->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
- DRM_CONNECTOR_POLL_DISCONNECT;
+ int ret = 0;
- ret = drm_connector_init(bridge->dev, &adv->connector,
- &adv7511_connector_funcs,
- DRM_MODE_CONNECTOR_HDMIA);
- if (ret) {
- DRM_ERROR("Failed to initialize connector with drm\n");
- return ret;
+ if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+ ret = adv7511_connector_init(adv);
+ if (ret < 0)
+ return ret;
}
- drm_connector_helper_add(&adv->connector,
- &adv7511_connector_helper_funcs);
- drm_connector_attach_encoder(&adv->connector, bridge->encoder);
if (adv->type == ADV7533 || adv->type == ADV7535)
ret = adv7533_attach_dsi(adv);
@@ -890,11 +920,38 @@ static int adv7511_bridge_attach(struct drm_bridge *bridge,
return ret;
}
+static enum drm_connector_status adv7511_bridge_detect(struct drm_bridge *bridge)
+{
+ struct adv7511 *adv = bridge_to_adv7511(bridge);
+
+ return adv7511_detect(adv, NULL);
+}
+
+static struct edid *adv7511_bridge_get_edid(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct adv7511 *adv = bridge_to_adv7511(bridge);
+
+ return adv7511_get_edid(adv, connector);
+}
+
+static void adv7511_bridge_hpd_notify(struct drm_bridge *bridge,
+ enum drm_connector_status status)
+{
+ struct adv7511 *adv = bridge_to_adv7511(bridge);
+
+ if (status == connector_status_disconnected)
+ cec_phys_addr_invalidate(adv->cec_adap);
+}
+
static const struct drm_bridge_funcs adv7511_bridge_funcs = {
.enable = adv7511_bridge_enable,
.disable = adv7511_bridge_disable,
.mode_set = adv7511_bridge_mode_set,
.attach = adv7511_bridge_attach,
+ .detect = adv7511_bridge_detect,
+ .get_edid = adv7511_bridge_get_edid,
+ .hpd_notify = adv7511_bridge_hpd_notify,
};
/* -----------------------------------------------------------------------------
@@ -1223,6 +1280,8 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
goto err_unregister_cec;
adv7511->bridge.funcs = &adv7511_bridge_funcs;
+ adv7511->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
+ | DRM_BRIDGE_OP_HPD;
adv7511->bridge.of_node = dev->of_node;
adv7511->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
index 9af39ec958db..f082b4ed4878 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
@@ -588,6 +588,7 @@ static int anx6345_bridge_attach(struct drm_bridge *bridge,
static enum drm_mode_status
anx6345_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c
index 0d5a5ad0c9ee..81debd02c169 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c
@@ -944,6 +944,7 @@ static int anx78xx_bridge_attach(struct drm_bridge *bridge,
static enum drm_mode_status
anx78xx_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c
index 69c3892caee5..76373e31df92 100644
--- a/drivers/gpu/drm/bridge/cdns-dsi.c
+++ b/drivers/gpu/drm/bridge/cdns-dsi.c
@@ -663,6 +663,7 @@ static int cdns_dsi_bridge_attach(struct drm_bridge *bridge,
static enum drm_mode_status
cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge);
diff --git a/drivers/gpu/drm/bridge/chrontel-ch7033.c b/drivers/gpu/drm/bridge/chrontel-ch7033.c
index f8675d82974b..486f405c2e16 100644
--- a/drivers/gpu/drm/bridge/chrontel-ch7033.c
+++ b/drivers/gpu/drm/bridge/chrontel-ch7033.c
@@ -317,6 +317,7 @@ static void ch7033_bridge_detach(struct drm_bridge *bridge)
}
static enum drm_mode_status ch7033_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
if (mode->clock > 165000)
diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c
index c7bc194bbce3..ce94f797d090 100644
--- a/drivers/gpu/drm/bridge/nwl-dsi.c
+++ b/drivers/gpu/drm/bridge/nwl-dsi.c
@@ -818,6 +818,7 @@ static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
static enum drm_mode_status
nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
struct nwl_dsi *dsi = bridge_to_dsi(bridge);
diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c
index 6dad025f8da7..19d8ae59ea03 100644
--- a/drivers/gpu/drm/bridge/sii902x.c
+++ b/drivers/gpu/drm/bridge/sii902x.c
@@ -360,7 +360,7 @@ static void sii902x_bridge_mode_set(struct drm_bridge *bridge,
buf[0] = pixel_clock_10kHz & 0xff;
buf[1] = pixel_clock_10kHz >> 8;
- buf[2] = adj->vrefresh;
+ buf[2] = drm_mode_vrefresh(adj);
buf[3] = 0x00;
buf[4] = adj->hdisplay;
buf[5] = adj->hdisplay >> 8;
diff --git a/drivers/gpu/drm/bridge/sii9234.c b/drivers/gpu/drm/bridge/sii9234.c
index b1258f0ed205..15c98a7bd81c 100644
--- a/drivers/gpu/drm/bridge/sii9234.c
+++ b/drivers/gpu/drm/bridge/sii9234.c
@@ -873,6 +873,7 @@ static inline struct sii9234 *bridge_to_sii9234(struct drm_bridge *bridge)
}
static enum drm_mode_status sii9234_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
if (mode->clock > MHL1_MAX_CLK)
diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c
index a282dc962199..843265d7f1b1 100644
--- a/drivers/gpu/drm/bridge/sil-sii8620.c
+++ b/drivers/gpu/drm/bridge/sil-sii8620.c
@@ -178,7 +178,7 @@ static void sii8620_read_buf(struct sii8620 *ctx, u16 addr, u8 *buf, int len)
static u8 sii8620_readb(struct sii8620 *ctx, u16 addr)
{
- u8 ret;
+ u8 ret = 0;
sii8620_read_buf(ctx, addr, &ret, 1);
return ret;
@@ -2244,6 +2244,7 @@ static int sii8620_is_packing_required(struct sii8620 *ctx,
}
static enum drm_mode_status sii8620_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
struct sii8620 *ctx = bridge_to_sii8620(bridge);
diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c
index a2dca7a3ef03..d974282c12b2 100644
--- a/drivers/gpu/drm/bridge/simple-bridge.c
+++ b/drivers/gpu/drm/bridge/simple-bridge.c
@@ -29,7 +29,7 @@ struct simple_bridge {
const struct simple_bridge_info *info;
- struct i2c_adapter *ddc;
+ struct drm_bridge *next_bridge;
struct regulator *vdd;
struct gpio_desc *enable;
};
@@ -52,29 +52,28 @@ static int simple_bridge_get_modes(struct drm_connector *connector)
struct edid *edid;
int ret;
- if (!sbridge->ddc)
- goto fallback;
+ if (sbridge->next_bridge->ops & DRM_BRIDGE_OP_EDID) {
+ edid = drm_bridge_get_edid(sbridge->next_bridge, connector);
+ if (!edid)
+ DRM_INFO("EDID read failed. Fallback to standard modes\n");
+ } else {
+ edid = NULL;
+ }
- edid = drm_get_edid(connector, sbridge->ddc);
if (!edid) {
- DRM_INFO("EDID readout failed, falling back to standard modes\n");
- goto fallback;
+ /*
+ * In case we cannot retrieve the EDIDs (missing or broken DDC
+ * bus from the next bridge), fallback on the XGA standards and
+ * prefer a mode pretty much anyone can handle.
+ */
+ ret = drm_add_modes_noedid(connector, 1920, 1200);
+ drm_set_preferred_mode(connector, 1024, 768);
+ return ret;
}
drm_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
kfree(edid);
- return ret;
-
-fallback:
- /*
- * In case we cannot retrieve the EDIDs (broken or missing i2c
- * bus), fallback on the XGA standards
- */
- ret = drm_add_modes_noedid(connector, 1920, 1200);
-
- /* And prefer a mode pretty much anyone can handle */
- drm_set_preferred_mode(connector, 1024, 768);
return ret;
}
@@ -88,16 +87,7 @@ simple_bridge_connector_detect(struct drm_connector *connector, bool force)
{
struct simple_bridge *sbridge = drm_connector_to_simple_bridge(connector);
- /*
- * Even if we have an I2C bus, we can't assume that the cable
- * is disconnected if drm_probe_ddc fails. Some cables don't
- * wire the DDC pins, or the I2C bus might not be working at
- * all.
- */
- if (sbridge->ddc && drm_probe_ddc(sbridge->ddc))
- return connector_status_connected;
-
- return connector_status_unknown;
+ return drm_bridge_detect(sbridge->next_bridge);
}
static const struct drm_connector_funcs simple_bridge_con_funcs = {
@@ -115,10 +105,13 @@ static int simple_bridge_attach(struct drm_bridge *bridge,
struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
int ret;
- if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
- DRM_ERROR("Fix bridge driver to make connector optional!");
- return -EINVAL;
- }
+ ret = drm_bridge_attach(bridge->encoder, sbridge->next_bridge, bridge,
+ DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+ if (ret < 0)
+ return ret;
+
+ if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
+ return 0;
if (!bridge->encoder) {
DRM_ERROR("Missing encoder\n");
@@ -130,14 +123,13 @@ static int simple_bridge_attach(struct drm_bridge *bridge,
ret = drm_connector_init_with_ddc(bridge->dev, &sbridge->connector,
&simple_bridge_con_funcs,
sbridge->info->connector_type,
- sbridge->ddc);
+ sbridge->next_bridge->ddc);
if (ret) {
DRM_ERROR("Failed to initialize connector\n");
return ret;
}
- drm_connector_attach_encoder(&sbridge->connector,
- bridge->encoder);
+ drm_connector_attach_encoder(&sbridge->connector, bridge->encoder);
return 0;
}
@@ -172,31 +164,10 @@ static const struct drm_bridge_funcs simple_bridge_bridge_funcs = {
.disable = simple_bridge_disable,
};
-static struct i2c_adapter *simple_bridge_retrieve_ddc(struct device *dev)
-{
- struct device_node *phandle, *remote;
- struct i2c_adapter *ddc;
-
- remote = of_graph_get_remote_node(dev->of_node, 1, -1);
- if (!remote)
- return ERR_PTR(-EINVAL);
-
- phandle = of_parse_phandle(remote, "ddc-i2c-bus", 0);
- of_node_put(remote);
- if (!phandle)
- return ERR_PTR(-ENODEV);
-
- ddc = of_get_i2c_adapter_by_node(phandle);
- of_node_put(phandle);
- if (!ddc)
- return ERR_PTR(-EPROBE_DEFER);
-
- return ddc;
-}
-
static int simple_bridge_probe(struct platform_device *pdev)
{
struct simple_bridge *sbridge;
+ struct device_node *remote;
sbridge = devm_kzalloc(&pdev->dev, sizeof(*sbridge), GFP_KERNEL);
if (!sbridge)
@@ -205,6 +176,20 @@ static int simple_bridge_probe(struct platform_device *pdev)
sbridge->info = of_device_get_match_data(&pdev->dev);
+ /* Get the next bridge in the pipeline. */
+ remote = of_graph_get_remote_node(pdev->dev.of_node, 1, -1);
+ if (!remote)
+ return -EINVAL;
+
+ sbridge->next_bridge = of_drm_find_bridge(remote);
+ of_node_put(remote);
+
+ if (!sbridge->next_bridge) {
+ dev_dbg(&pdev->dev, "Next bridge not found, deferring probe\n");
+ return -EPROBE_DEFER;
+ }
+
+ /* Get the regulator and GPIO resources. */
sbridge->vdd = devm_regulator_get_optional(&pdev->dev, "vdd");
if (IS_ERR(sbridge->vdd)) {
int ret = PTR_ERR(sbridge->vdd);
@@ -222,18 +207,7 @@ static int simple_bridge_probe(struct platform_device *pdev)
return PTR_ERR(sbridge->enable);
}
- sbridge->ddc = simple_bridge_retrieve_ddc(&pdev->dev);
- if (IS_ERR(sbridge->ddc)) {
- if (PTR_ERR(sbridge->ddc) == -ENODEV) {
- dev_dbg(&pdev->dev,
- "No i2c bus specified. Disabling EDID readout\n");
- sbridge->ddc = NULL;
- } else {
- dev_err(&pdev->dev, "Couldn't retrieve i2c bus\n");
- return PTR_ERR(sbridge->ddc);
- }
- }
-
+ /* Register the bridge. */
sbridge->bridge.funcs = &simple_bridge_bridge_funcs;
sbridge->bridge.of_node = pdev->dev.of_node;
sbridge->bridge.timings = sbridge->info->timings;
@@ -249,9 +223,6 @@ static int simple_bridge_remove(struct platform_device *pdev)
drm_bridge_remove(&sbridge->bridge);
- if (sbridge->ddc)
- i2c_put_adapter(sbridge->ddc);
-
return 0;
}
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 30681398cfb0..748df1cacd2b 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -181,6 +181,7 @@ struct dw_hdmi {
struct mutex mutex; /* for state below and previous_mode */
enum drm_connector_force force; /* mutex-protected force state */
+ struct drm_connector *curr_conn;/* current connector (only valid when !disabled) */
bool disabled; /* DRM has disabled our bridge */
bool bridge_is_on; /* indicates the bridge is on */
bool rxsense; /* rxsense state */
@@ -1241,10 +1242,9 @@ void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_write);
/* Filter out invalid setups to avoid configuring SCDC and scrambling */
-static bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi)
+static bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi,
+ const struct drm_display_info *display)
{
- struct drm_display_info *display = &hdmi->connector.display_info;
-
/* Completely disable SCDC support for older controllers */
if (hdmi->version < 0x200a)
return false;
@@ -1282,12 +1282,13 @@ static bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi)
* helper should called right before enabling the TMDS Clock and Data in
* the PHY configuration callback.
*/
-void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi)
+void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi,
+ const struct drm_display_info *display)
{
unsigned long mtmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
/* Control for TMDS Bit Period/TMDS Clock-Period Ratio */
- if (dw_hdmi_support_scdc(hdmi)) {
+ if (dw_hdmi_support_scdc(hdmi, display)) {
if (mtmdsclock > HDMI14_MAX_TMDSCLK)
drm_scdc_set_high_tmds_clock_ratio(hdmi->ddc, 1);
else
@@ -1490,7 +1491,8 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
return 0;
}
-static int hdmi_phy_configure(struct dw_hdmi *hdmi)
+static int hdmi_phy_configure(struct dw_hdmi *hdmi,
+ const struct drm_display_info *display)
{
const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
@@ -1500,7 +1502,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
dw_hdmi_phy_power_off(hdmi);
- dw_hdmi_set_high_tmds_clock_ratio(hdmi);
+ dw_hdmi_set_high_tmds_clock_ratio(hdmi, display);
/* Leave low power consumption mode by asserting SVSRET. */
if (phy->has_svsret)
@@ -1514,7 +1516,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
/* Write to the PHY as configured by the platform */
if (pdata->configure_phy)
- ret = pdata->configure_phy(hdmi, pdata, mpixelclock);
+ ret = pdata->configure_phy(hdmi, pdata->priv_data, mpixelclock);
else
ret = phy->configure(hdmi, pdata, mpixelclock);
if (ret) {
@@ -1531,7 +1533,8 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
}
static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
- struct drm_display_mode *mode)
+ const struct drm_display_info *display,
+ const struct drm_display_mode *mode)
{
int i, ret;
@@ -1540,7 +1543,7 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
dw_hdmi_phy_sel_data_en_pol(hdmi, 1);
dw_hdmi_phy_sel_interface_control(hdmi, 0);
- ret = hdmi_phy_configure(hdmi);
+ ret = hdmi_phy_configure(hdmi, display);
if (ret)
return ret;
}
@@ -1628,18 +1631,18 @@ static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi)
HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1);
}
-static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
+static void hdmi_config_AVI(struct dw_hdmi *hdmi,
+ const struct drm_connector *connector,
+ const struct drm_display_mode *mode)
{
struct hdmi_avi_infoframe frame;
u8 val;
/* Initialise info frame from DRM mode */
- drm_hdmi_avi_infoframe_from_display_mode(&frame,
- &hdmi->connector, mode);
+ drm_hdmi_avi_infoframe_from_display_mode(&frame, connector, mode);
if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) {
- drm_hdmi_avi_infoframe_quant_range(&frame, &hdmi->connector,
- mode,
+ drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode,
hdmi->hdmi_data.rgb_limited_range ?
HDMI_QUANTIZATION_RANGE_LIMITED :
HDMI_QUANTIZATION_RANGE_FULL);
@@ -1756,14 +1759,14 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
}
static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi,
- struct drm_display_mode *mode)
+ const struct drm_connector *connector,
+ const struct drm_display_mode *mode)
{
struct hdmi_vendor_infoframe frame;
u8 buffer[10];
ssize_t err;
- err = drm_hdmi_vendor_infoframe_from_display_mode(&frame,
- &hdmi->connector,
+ err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, connector,
mode);
if (err < 0)
/*
@@ -1809,9 +1812,10 @@ static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi,
HDMI_FC_DATAUTO0_VSD_MASK);
}
-static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi)
+static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi,
+ const struct drm_connector *connector)
{
- const struct drm_connector_state *conn_state = hdmi->connector.state;
+ const struct drm_connector_state *conn_state = connector->state;
struct hdmi_drm_infoframe frame;
u8 buffer[30];
ssize_t err;
@@ -1845,10 +1849,11 @@ static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi)
}
static void hdmi_av_composer(struct dw_hdmi *hdmi,
+ const struct drm_display_info *display,
const struct drm_display_mode *mode)
{
u8 inv_val, bytes;
- struct drm_hdmi_info *hdmi_info = &hdmi->connector.display_info.hdmi;
+ const struct drm_hdmi_info *hdmi_info = &display->hdmi;
struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
unsigned int vdisplay, hdisplay;
@@ -1881,7 +1886,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
/* Set up HDMI_FC_INVIDCONF */
inv_val = (hdmi->hdmi_data.hdcp_enable ||
- (dw_hdmi_support_scdc(hdmi) &&
+ (dw_hdmi_support_scdc(hdmi, display) &&
(vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
hdmi_info->scdc.scrambling.low_rates)) ?
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
@@ -1949,7 +1954,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
}
/* Scrambling Control */
- if (dw_hdmi_support_scdc(hdmi)) {
+ if (dw_hdmi_support_scdc(hdmi, display)) {
if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
hdmi_info->scdc.scrambling.low_rates) {
/*
@@ -2112,7 +2117,9 @@ static void hdmi_disable_overflow_interrupts(struct dw_hdmi *hdmi)
HDMI_IH_MUTE_FC_STAT2);
}
-static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
+static int dw_hdmi_setup(struct dw_hdmi *hdmi,
+ const struct drm_connector *connector,
+ const struct drm_display_mode *mode)
{
int ret;
@@ -2137,10 +2144,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
- if (hdmi->plat_data->input_bus_format)
- hdmi->hdmi_data.enc_in_bus_format =
- hdmi->plat_data->input_bus_format;
- else if (hdmi->hdmi_data.enc_in_bus_format == MEDIA_BUS_FMT_FIXED)
+ if (hdmi->hdmi_data.enc_in_bus_format == MEDIA_BUS_FMT_FIXED)
hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
/* TOFIX: Get input encoding from plat data or fallback to none */
@@ -2162,10 +2166,12 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
hdmi->hdmi_data.video_mode.mdataenablepolarity = true;
/* HDMI Initialization Step B.1 */
- hdmi_av_composer(hdmi, mode);
+ hdmi_av_composer(hdmi, &connector->display_info, mode);
/* HDMI Initializateion Step B.2 */
- ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, &hdmi->previous_mode);
+ ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data,
+ &connector->display_info,
+ &hdmi->previous_mode);
if (ret)
return ret;
hdmi->phy.enabled = true;
@@ -2186,9 +2192,9 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
dev_dbg(hdmi->dev, "%s HDMI mode\n", __func__);
/* HDMI Initialization Step F - Configure AVI InfoFrame */
- hdmi_config_AVI(hdmi, mode);
- hdmi_config_vendor_specific_infoframe(hdmi, mode);
- hdmi_config_drm_infoframe(hdmi);
+ hdmi_config_AVI(hdmi, connector, mode);
+ hdmi_config_vendor_specific_infoframe(hdmi, connector, mode);
+ hdmi_config_drm_infoframe(hdmi, connector);
} else {
dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
}
@@ -2257,7 +2263,12 @@ static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi)
static void dw_hdmi_poweron(struct dw_hdmi *hdmi)
{
hdmi->bridge_is_on = true;
- dw_hdmi_setup(hdmi, &hdmi->previous_mode);
+
+ /*
+ * The curr_conn field is guaranteed to be valid here, as this function
+ * is only be called when !hdmi->disabled.
+ */
+ dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode);
}
static void dw_hdmi_poweroff(struct dw_hdmi *hdmi)
@@ -2312,11 +2323,8 @@ static void dw_hdmi_update_phy_mask(struct dw_hdmi *hdmi)
hdmi->rxsense);
}
-static enum drm_connector_status
-dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
+static enum drm_connector_status dw_hdmi_detect(struct dw_hdmi *hdmi)
{
- struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
- connector);
enum drm_connector_status result;
mutex_lock(&hdmi->mutex);
@@ -2339,31 +2347,57 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
return result;
}
-static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
+static struct edid *dw_hdmi_get_edid(struct dw_hdmi *hdmi,
+ struct drm_connector *connector)
{
- struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
- connector);
struct edid *edid;
- int ret = 0;
if (!hdmi->ddc)
- return 0;
+ return NULL;
edid = drm_get_edid(connector, hdmi->ddc);
- if (edid) {
- dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
- edid->width_cm, edid->height_cm);
-
- hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
- hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
- drm_connector_update_edid_property(connector, edid);
- cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid);
- ret = drm_add_edid_modes(connector, edid);
- kfree(edid);
- } else {
+ if (!edid) {
dev_dbg(hdmi->dev, "failed to get edid\n");
+ return NULL;
}
+ dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
+ edid->width_cm, edid->height_cm);
+
+ hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+ hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
+
+ return edid;
+}
+
+/* -----------------------------------------------------------------------------
+ * DRM Connector Operations
+ */
+
+static enum drm_connector_status
+dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+ connector);
+ return dw_hdmi_detect(hdmi);
+}
+
+static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+ struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+ connector);
+ struct edid *edid;
+ int ret;
+
+ edid = dw_hdmi_get_edid(hdmi, connector);
+ if (!edid)
+ return 0;
+
+ drm_connector_update_edid_property(connector, edid);
+ cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid);
+ ret = drm_add_edid_modes(connector, edid);
+ kfree(edid);
+
return ret;
}
@@ -2433,6 +2467,59 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs =
.atomic_check = dw_hdmi_connector_atomic_check,
};
+static int dw_hdmi_connector_create(struct dw_hdmi *hdmi)
+{
+ struct drm_connector *connector = &hdmi->connector;
+ struct cec_connector_info conn_info;
+ struct cec_notifier *notifier;
+
+ if (hdmi->version >= 0x200a)
+ connector->ycbcr_420_allowed =
+ hdmi->plat_data->ycbcr_420_allowed;
+ else
+ connector->ycbcr_420_allowed = false;
+
+ connector->interlace_allowed = 1;
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+ drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs);
+
+ drm_connector_init_with_ddc(hdmi->bridge.dev, connector,
+ &dw_hdmi_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ hdmi->ddc);
+
+ /*
+ * drm_connector_attach_max_bpc_property() requires the
+ * connector to have a state.
+ */
+ drm_atomic_helper_connector_reset(connector);
+
+ drm_connector_attach_max_bpc_property(connector, 8, 16);
+
+ if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe)
+ drm_object_attach_property(&connector->base,
+ connector->dev->mode_config.hdr_output_metadata_property, 0);
+
+ drm_connector_attach_encoder(connector, hdmi->bridge.encoder);
+
+ cec_fill_conn_info_from_drm(&conn_info, connector);
+
+ notifier = cec_notifier_conn_register(hdmi->dev, NULL, &conn_info);
+ if (!notifier)
+ return -ENOMEM;
+
+ mutex_lock(&hdmi->cec_notifier_mutex);
+ hdmi->cec_notifier = notifier;
+ mutex_unlock(&hdmi->cec_notifier_mutex);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DRM Bridge Operations
+ */
+
/*
* Possible output formats :
* - MEDIA_BUS_FMT_UYYVYY16_0_5X48,
@@ -2708,51 +2795,11 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct dw_hdmi *hdmi = bridge->driver_private;
- struct drm_encoder *encoder = bridge->encoder;
- struct drm_connector *connector = &hdmi->connector;
- struct cec_connector_info conn_info;
- struct cec_notifier *notifier;
- if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
- DRM_ERROR("Fix bridge driver to make connector optional!");
- return -EINVAL;
- }
-
- connector->interlace_allowed = 1;
- connector->polled = DRM_CONNECTOR_POLL_HPD;
-
- drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs);
-
- drm_connector_init_with_ddc(bridge->dev, connector,
- &dw_hdmi_connector_funcs,
- DRM_MODE_CONNECTOR_HDMIA,
- hdmi->ddc);
-
- /*
- * drm_connector_attach_max_bpc_property() requires the
- * connector to have a state.
- */
- drm_atomic_helper_connector_reset(connector);
-
- drm_connector_attach_max_bpc_property(connector, 8, 16);
-
- if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe)
- drm_object_attach_property(&connector->base,
- connector->dev->mode_config.hdr_output_metadata_property, 0);
-
- drm_connector_attach_encoder(connector, encoder);
-
- cec_fill_conn_info_from_drm(&conn_info, connector);
-
- notifier = cec_notifier_conn_register(hdmi->dev, NULL, &conn_info);
- if (!notifier)
- return -ENOMEM;
-
- mutex_lock(&hdmi->cec_notifier_mutex);
- hdmi->cec_notifier = notifier;
- mutex_unlock(&hdmi->cec_notifier_mutex);
+ if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
+ return 0;
- return 0;
+ return dw_hdmi_connector_create(hdmi);
}
static void dw_hdmi_bridge_detach(struct drm_bridge *bridge)
@@ -2767,18 +2814,20 @@ static void dw_hdmi_bridge_detach(struct drm_bridge *bridge)
static enum drm_mode_status
dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
struct dw_hdmi *hdmi = bridge->driver_private;
- struct drm_connector *connector = &hdmi->connector;
+ const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
enum drm_mode_status mode_status = MODE_OK;
/* We don't support double-clocked modes */
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
return MODE_BAD;
- if (hdmi->plat_data->mode_valid)
- mode_status = hdmi->plat_data->mode_valid(connector, mode);
+ if (pdata->mode_valid)
+ mode_status = pdata->mode_valid(hdmi, pdata->priv_data, info,
+ mode);
return mode_status;
}
@@ -2797,28 +2846,52 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
mutex_unlock(&hdmi->mutex);
}
-static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
+static void dw_hdmi_bridge_atomic_disable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_state)
{
struct dw_hdmi *hdmi = bridge->driver_private;
mutex_lock(&hdmi->mutex);
hdmi->disabled = true;
+ hdmi->curr_conn = NULL;
dw_hdmi_update_power(hdmi);
dw_hdmi_update_phy_mask(hdmi);
mutex_unlock(&hdmi->mutex);
}
-static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
+static void dw_hdmi_bridge_atomic_enable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_state)
{
struct dw_hdmi *hdmi = bridge->driver_private;
+ struct drm_atomic_state *state = old_state->base.state;
+ struct drm_connector *connector;
+
+ connector = drm_atomic_get_new_connector_for_encoder(state,
+ bridge->encoder);
mutex_lock(&hdmi->mutex);
hdmi->disabled = false;
+ hdmi->curr_conn = connector;
dw_hdmi_update_power(hdmi);
dw_hdmi_update_phy_mask(hdmi);
mutex_unlock(&hdmi->mutex);
}
+static enum drm_connector_status dw_hdmi_bridge_detect(struct drm_bridge *bridge)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+
+ return dw_hdmi_detect(hdmi);
+}
+
+static struct edid *dw_hdmi_bridge_get_edid(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+
+ return dw_hdmi_get_edid(hdmi, connector);
+}
+
static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
@@ -2828,12 +2901,18 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
.atomic_check = dw_hdmi_bridge_atomic_check,
.atomic_get_output_bus_fmts = dw_hdmi_bridge_atomic_get_output_bus_fmts,
.atomic_get_input_bus_fmts = dw_hdmi_bridge_atomic_get_input_bus_fmts,
- .enable = dw_hdmi_bridge_enable,
- .disable = dw_hdmi_bridge_disable,
+ .atomic_enable = dw_hdmi_bridge_atomic_enable,
+ .atomic_disable = dw_hdmi_bridge_atomic_disable,
.mode_set = dw_hdmi_bridge_mode_set,
.mode_valid = dw_hdmi_bridge_mode_valid,
+ .detect = dw_hdmi_bridge_detect,
+ .get_edid = dw_hdmi_bridge_get_edid,
};
+/* -----------------------------------------------------------------------------
+ * IRQ Handling
+ */
+
static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi)
{
struct dw_hdmi_i2c *i2c = hdmi->i2c;
@@ -2943,10 +3022,18 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
}
if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
+ enum drm_connector_status status = phy_int_pol & HDMI_PHY_HPD
+ ? connector_status_connected
+ : connector_status_disconnected;
+
dev_dbg(hdmi->dev, "EVENT=%s\n",
- phy_int_pol & HDMI_PHY_HPD ? "plugin" : "plugout");
- if (hdmi->bridge.dev)
+ status == connector_status_connected ?
+ "plugin" : "plugout");
+
+ if (hdmi->bridge.dev) {
drm_helper_hpd_irq_event(hdmi->bridge.dev);
+ drm_bridge_hpd_notify(&hdmi->bridge, status);
+ }
}
hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
@@ -3092,9 +3179,11 @@ static void dw_hdmi_init_hw(struct dw_hdmi *hdmi)
hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data);
}
-static struct dw_hdmi *
-__dw_hdmi_probe(struct platform_device *pdev,
- const struct dw_hdmi_plat_data *plat_data)
+/* -----------------------------------------------------------------------------
+ * Probe/remove API, used from platforms based on the DRM bridge API.
+ */
+struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
+ const struct dw_hdmi_plat_data *plat_data)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
@@ -3292,16 +3381,12 @@ __dw_hdmi_probe(struct platform_device *pdev,
hdmi->bridge.driver_private = hdmi;
hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
+ hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
+ | DRM_BRIDGE_OP_HPD;
#ifdef CONFIG_OF
hdmi->bridge.of_node = pdev->dev.of_node;
#endif
- if (hdmi->version >= 0x200a)
- hdmi->connector.ycbcr_420_allowed =
- hdmi->plat_data->ycbcr_420_allowed;
- else
- hdmi->connector.ycbcr_420_allowed = false;
-
memset(&pdevinfo, 0, sizeof(pdevinfo));
pdevinfo.parent = dev;
pdevinfo.id = PLATFORM_DEVID_AUTO;
@@ -3355,14 +3440,11 @@ __dw_hdmi_probe(struct platform_device *pdev,
hdmi->cec = platform_device_register_full(&pdevinfo);
}
+ drm_bridge_add(&hdmi->bridge);
+
return hdmi;
err_iahb:
- if (hdmi->i2c) {
- i2c_del_adapter(&hdmi->i2c->adap);
- hdmi->ddc = NULL;
- }
-
clk_disable_unprepare(hdmi->iahb_clk);
if (hdmi->cec_clk)
clk_disable_unprepare(hdmi->cec_clk);
@@ -3373,9 +3455,12 @@ err_res:
return ERR_PTR(ret);
}
+EXPORT_SYMBOL_GPL(dw_hdmi_probe);
-static void __dw_hdmi_remove(struct dw_hdmi *hdmi)
+void dw_hdmi_remove(struct dw_hdmi *hdmi)
{
+ drm_bridge_remove(&hdmi->bridge);
+
if (hdmi->audio && !IS_ERR(hdmi->audio))
platform_device_unregister(hdmi->audio);
if (!IS_ERR(hdmi->cec))
@@ -3394,31 +3479,6 @@ static void __dw_hdmi_remove(struct dw_hdmi *hdmi)
else
i2c_put_adapter(hdmi->ddc);
}
-
-/* -----------------------------------------------------------------------------
- * Probe/remove API, used from platforms based on the DRM bridge API.
- */
-struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
- const struct dw_hdmi_plat_data *plat_data)
-{
- struct dw_hdmi *hdmi;
-
- hdmi = __dw_hdmi_probe(pdev, plat_data);
- if (IS_ERR(hdmi))
- return hdmi;
-
- drm_bridge_add(&hdmi->bridge);
-
- return hdmi;
-}
-EXPORT_SYMBOL_GPL(dw_hdmi_probe);
-
-void dw_hdmi_remove(struct dw_hdmi *hdmi)
-{
- drm_bridge_remove(&hdmi->bridge);
-
- __dw_hdmi_remove(hdmi);
-}
EXPORT_SYMBOL_GPL(dw_hdmi_remove);
/* -----------------------------------------------------------------------------
@@ -3431,7 +3491,7 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
struct dw_hdmi *hdmi;
int ret;
- hdmi = __dw_hdmi_probe(pdev, plat_data);
+ hdmi = dw_hdmi_probe(pdev, plat_data);
if (IS_ERR(hdmi))
return hdmi;
@@ -3448,7 +3508,7 @@ EXPORT_SYMBOL_GPL(dw_hdmi_bind);
void dw_hdmi_unbind(struct dw_hdmi *hdmi)
{
- __dw_hdmi_remove(hdmi);
+ dw_hdmi_remove(hdmi);
}
EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
index 5ef0f154aa7b..d580b2aa4ce9 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
@@ -27,7 +27,6 @@
#include <drm/drm_modes.h>
#include <drm/drm_of.h>
#include <drm/drm_print.h>
-#include <drm/drm_probe_helper.h>
#define HWVER_131 0x31333100 /* IP version 1.31 */
@@ -924,6 +923,7 @@ static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge)
static enum drm_mode_status
dw_mipi_dsi_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c
index e4c0ea03ae3a..c2777b226c75 100644
--- a/drivers/gpu/drm/bridge/tc358767.c
+++ b/drivers/gpu/drm/bridge/tc358767.c
@@ -1306,6 +1306,7 @@ static bool tc_bridge_mode_fixup(struct drm_bridge *bridge,
}
static enum drm_mode_status tc_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
struct tc_data *tc = bridge_to_tc(bridge);
diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c
index 6650fe4cfc20..8ed8302d6bbb 100644
--- a/drivers/gpu/drm/bridge/tc358768.c
+++ b/drivers/gpu/drm/bridge/tc358768.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com
* Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*/
@@ -529,6 +529,7 @@ static int tc358768_bridge_attach(struct drm_bridge *bridge,
static enum drm_mode_status
tc358768_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
struct tc358768_priv *priv = bridge_to_tc358768(bridge);
diff --git a/drivers/gpu/drm/bridge/thc63lvd1024.c b/drivers/gpu/drm/bridge/thc63lvd1024.c
index 97d8129760e9..86b06975bfdd 100644
--- a/drivers/gpu/drm/bridge/thc63lvd1024.c
+++ b/drivers/gpu/drm/bridge/thc63lvd1024.c
@@ -51,6 +51,7 @@ static int thc63_attach(struct drm_bridge *bridge,
}
static enum drm_mode_status thc63_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
struct thc63_dev *thc63 = to_thc63(bridge);
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
index 6ad688b320ae..86b9f0f87a14 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
@@ -1,12 +1,14 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
- * datasheet: http://www.ti.com/lit/ds/symlink/sn65dsi86.pdf
+ * datasheet: https://www.ti.com/lit/ds/symlink/sn65dsi86.pdf
*/
+#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
#include <linux/i2c.h>
#include <linux/iopoll.h>
#include <linux/module.h>
@@ -48,12 +50,24 @@
#define SN_CHA_VERTICAL_BACK_PORCH_REG 0x36
#define SN_CHA_HORIZONTAL_FRONT_PORCH_REG 0x38
#define SN_CHA_VERTICAL_FRONT_PORCH_REG 0x3A
+#define SN_LN_ASSIGN_REG 0x59
+#define LN_ASSIGN_WIDTH 2
#define SN_ENH_FRAME_REG 0x5A
#define VSTREAM_ENABLE BIT(3)
+#define LN_POLRS_OFFSET 4
+#define LN_POLRS_MASK 0xf0
#define SN_DATA_FORMAT_REG 0x5B
#define BPP_18_RGB BIT(0)
#define SN_HPD_DISABLE_REG 0x5C
#define HPD_DISABLE BIT(0)
+#define SN_GPIO_IO_REG 0x5E
+#define SN_GPIO_INPUT_SHIFT 4
+#define SN_GPIO_OUTPUT_SHIFT 0
+#define SN_GPIO_CTRL_REG 0x5F
+#define SN_GPIO_MUX_INPUT 0
+#define SN_GPIO_MUX_OUTPUT 1
+#define SN_GPIO_MUX_SPECIAL 2
+#define SN_GPIO_MUX_MASK 0x3
#define SN_AUX_WDATA_REG(x) (0x64 + (x))
#define SN_AUX_ADDR_19_16_REG 0x74
#define SN_AUX_ADDR_15_8_REG 0x75
@@ -88,6 +102,38 @@
#define SN_REGULATOR_SUPPLY_NUM 4
+#define SN_MAX_DP_LANES 4
+#define SN_NUM_GPIOS 4
+#define SN_GPIO_PHYSICAL_OFFSET 1
+
+/**
+ * struct ti_sn_bridge - Platform data for ti-sn65dsi86 driver.
+ * @dev: Pointer to our device.
+ * @regmap: Regmap for accessing i2c.
+ * @aux: Our aux channel.
+ * @bridge: Our bridge.
+ * @connector: Our connector.
+ * @debugfs: Used for managing our debugfs.
+ * @host_node: Remote DSI node.
+ * @dsi: Our MIPI DSI source.
+ * @refclk: Our reference clock.
+ * @panel: Our panel.
+ * @enable_gpio: The GPIO we toggle to enable the bridge.
+ * @supplies: Data for bulk enabling/disabling our regulators.
+ * @dp_lanes: Count of dp_lanes we're using.
+ * @ln_assign: Value to program to the LN_ASSIGN register.
+ * @ln_polrs: Value for the 4-bit LN_POLRS field of SN_ENH_FRAME_REG.
+ *
+ * @gchip: If we expose our GPIOs, this is used.
+ * @gchip_output: A cache of whether we've set GPIOs to output. This
+ * serves double-duty of keeping track of the direction and
+ * also keeping track of whether we've incremented the
+ * pm_runtime reference count for this pin, which we do
+ * whenever a pin is configured as an output. This is a
+ * bitmap so we can do atomic ops on it without an extra
+ * lock so concurrent users of our 4 GPIOs don't stomp on
+ * each other's read-modify-write.
+ */
struct ti_sn_bridge {
struct device *dev;
struct regmap *regmap;
@@ -102,6 +148,13 @@ struct ti_sn_bridge {
struct gpio_desc *enable_gpio;
struct regulator_bulk_data supplies[SN_REGULATOR_SUPPLY_NUM];
int dp_lanes;
+ u8 ln_assign;
+ u8 ln_polrs;
+
+#if defined(CONFIG_OF_GPIO)
+ struct gpio_chip gchip;
+ DECLARE_BITMAP(gchip_output, SN_NUM_GPIOS);
+#endif
};
static const struct regmap_range ti_sn_bridge_volatile_ranges[] = {
@@ -159,6 +212,8 @@ static int __maybe_unused ti_sn_bridge_suspend(struct device *dev)
static const struct dev_pm_ops ti_sn_bridge_pm_ops = {
SET_RUNTIME_PM_OPS(ti_sn_bridge_suspend, ti_sn_bridge_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
};
static int status_show(struct seq_file *s, void *data)
@@ -451,7 +506,7 @@ static unsigned int ti_sn_bridge_get_bpp(struct ti_sn_bridge *pdata)
return 24;
}
-/**
+/*
* LUT index corresponds to register value and
* LUT values corresponds to dp data rate supported
* by the bridge in Mbps unit.
@@ -475,7 +530,7 @@ static int ti_sn_bridge_calc_min_dp_rate_idx(struct ti_sn_bridge *pdata)
1000 * pdata->dp_lanes * DP_CLK_FUDGE_DEN);
for (i = 1; i < ARRAY_SIZE(ti_sn_bridge_dp_rate_lut) - 1; i++)
- if (ti_sn_bridge_dp_rate_lut[i] > dp_rate_mhz)
+ if (ti_sn_bridge_dp_rate_lut[i] >= dp_rate_mhz)
break;
return i;
@@ -666,26 +721,20 @@ static void ti_sn_bridge_enable(struct drm_bridge *bridge)
int dp_rate_idx;
unsigned int val;
int ret = -EINVAL;
+ int max_dp_lanes;
- /*
- * Run with the maximum number of lanes that the DP sink supports.
- *
- * Depending use cases, we might want to revisit this later because:
- * - It's plausible that someone may have run fewer lines to the
- * sink than the sink actually supports, assuming that the lines
- * will just be driven at a higher rate.
- * - The DP spec seems to indicate that it's more important to minimize
- * the number of lanes than the link rate.
- *
- * If we do revisit, it would be important to measure the power impact.
- */
- pdata->dp_lanes = ti_sn_get_max_lanes(pdata);
+ max_dp_lanes = ti_sn_get_max_lanes(pdata);
+ pdata->dp_lanes = min(pdata->dp_lanes, max_dp_lanes);
/* DSI_A lane config */
- val = CHA_DSI_LANES(4 - pdata->dsi->lanes);
+ val = CHA_DSI_LANES(SN_MAX_DP_LANES - pdata->dsi->lanes);
regmap_update_bits(pdata->regmap, SN_DSI_LANES_REG,
CHA_DSI_LANES_MASK, val);
+ regmap_write(pdata->regmap, SN_LN_ASSIGN_REG, pdata->ln_assign);
+ regmap_update_bits(pdata->regmap, SN_ENH_FRAME_REG, LN_POLRS_MASK,
+ pdata->ln_polrs << LN_POLRS_OFFSET);
+
/* set dsi clk frequency value */
ti_sn_bridge_set_dsi_rate(pdata);
@@ -827,6 +876,12 @@ static ssize_t ti_sn_aux_transfer(struct drm_dp_aux *aux,
buf[i]);
}
+ /* Clear old status bits before start so we don't get confused */
+ regmap_write(pdata->regmap, SN_AUX_CMD_STATUS_REG,
+ AUX_IRQ_STATUS_NAT_I2C_FAIL |
+ AUX_IRQ_STATUS_AUX_RPLY_TOUT |
+ AUX_IRQ_STATUS_AUX_SHORT);
+
regmap_write(pdata->regmap, SN_AUX_CMD_REG, request_val | AUX_CMD_SEND);
ret = regmap_read_poll_timeout(pdata->regmap, SN_AUX_CMD_REG, val,
@@ -874,6 +929,236 @@ static int ti_sn_bridge_parse_dsi_host(struct ti_sn_bridge *pdata)
return 0;
}
+#if defined(CONFIG_OF_GPIO)
+
+static int tn_sn_bridge_of_xlate(struct gpio_chip *chip,
+ const struct of_phandle_args *gpiospec,
+ u32 *flags)
+{
+ if (WARN_ON(gpiospec->args_count < chip->of_gpio_n_cells))
+ return -EINVAL;
+
+ if (gpiospec->args[0] > chip->ngpio || gpiospec->args[0] < 1)
+ return -EINVAL;
+
+ if (flags)
+ *flags = gpiospec->args[1];
+
+ return gpiospec->args[0] - SN_GPIO_PHYSICAL_OFFSET;
+}
+
+static int ti_sn_bridge_gpio_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct ti_sn_bridge *pdata = gpiochip_get_data(chip);
+
+ /*
+ * We already have to keep track of the direction because we use
+ * that to figure out whether we've powered the device. We can
+ * just return that rather than (maybe) powering up the device
+ * to ask its direction.
+ */
+ return test_bit(offset, pdata->gchip_output) ?
+ GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+}
+
+static int ti_sn_bridge_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct ti_sn_bridge *pdata = gpiochip_get_data(chip);
+ unsigned int val;
+ int ret;
+
+ /*
+ * When the pin is an input we don't forcibly keep the bridge
+ * powered--we just power it on to read the pin. NOTE: part of
+ * the reason this works is that the bridge defaults (when
+ * powered back on) to all 4 GPIOs being configured as GPIO input.
+ * Also note that if something else is keeping the chip powered the
+ * pm_runtime functions are lightweight increments of a refcount.
+ */
+ pm_runtime_get_sync(pdata->dev);
+ ret = regmap_read(pdata->regmap, SN_GPIO_IO_REG, &val);
+ pm_runtime_put(pdata->dev);
+
+ if (ret)
+ return ret;
+
+ return !!(val & BIT(SN_GPIO_INPUT_SHIFT + offset));
+}
+
+static void ti_sn_bridge_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int val)
+{
+ struct ti_sn_bridge *pdata = gpiochip_get_data(chip);
+ int ret;
+
+ if (!test_bit(offset, pdata->gchip_output)) {
+ dev_err(pdata->dev, "Ignoring GPIO set while input\n");
+ return;
+ }
+
+ val &= 1;
+ ret = regmap_update_bits(pdata->regmap, SN_GPIO_IO_REG,
+ BIT(SN_GPIO_OUTPUT_SHIFT + offset),
+ val << (SN_GPIO_OUTPUT_SHIFT + offset));
+ if (ret)
+ dev_warn(pdata->dev,
+ "Failed to set bridge GPIO %u: %d\n", offset, ret);
+}
+
+static int ti_sn_bridge_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct ti_sn_bridge *pdata = gpiochip_get_data(chip);
+ int shift = offset * 2;
+ int ret;
+
+ if (!test_and_clear_bit(offset, pdata->gchip_output))
+ return 0;
+
+ ret = regmap_update_bits(pdata->regmap, SN_GPIO_CTRL_REG,
+ SN_GPIO_MUX_MASK << shift,
+ SN_GPIO_MUX_INPUT << shift);
+ if (ret) {
+ set_bit(offset, pdata->gchip_output);
+ return ret;
+ }
+
+ /*
+ * NOTE: if nobody else is powering the device this may fully power
+ * it off and when it comes back it will have lost all state, but
+ * that's OK because the default is input and we're now an input.
+ */
+ pm_runtime_put(pdata->dev);
+
+ return 0;
+}
+
+static int ti_sn_bridge_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int val)
+{
+ struct ti_sn_bridge *pdata = gpiochip_get_data(chip);
+ int shift = offset * 2;
+ int ret;
+
+ if (test_and_set_bit(offset, pdata->gchip_output))
+ return 0;
+
+ pm_runtime_get_sync(pdata->dev);
+
+ /* Set value first to avoid glitching */
+ ti_sn_bridge_gpio_set(chip, offset, val);
+
+ /* Set direction */
+ ret = regmap_update_bits(pdata->regmap, SN_GPIO_CTRL_REG,
+ SN_GPIO_MUX_MASK << shift,
+ SN_GPIO_MUX_OUTPUT << shift);
+ if (ret) {
+ clear_bit(offset, pdata->gchip_output);
+ pm_runtime_put(pdata->dev);
+ }
+
+ return ret;
+}
+
+static void ti_sn_bridge_gpio_free(struct gpio_chip *chip, unsigned int offset)
+{
+ /* We won't keep pm_runtime if we're input, so switch there on free */
+ ti_sn_bridge_gpio_direction_input(chip, offset);
+}
+
+static const char * const ti_sn_bridge_gpio_names[SN_NUM_GPIOS] = {
+ "GPIO1", "GPIO2", "GPIO3", "GPIO4"
+};
+
+static int ti_sn_setup_gpio_controller(struct ti_sn_bridge *pdata)
+{
+ int ret;
+
+ /* Only init if someone is going to use us as a GPIO controller */
+ if (!of_property_read_bool(pdata->dev->of_node, "gpio-controller"))
+ return 0;
+
+ pdata->gchip.label = dev_name(pdata->dev);
+ pdata->gchip.parent = pdata->dev;
+ pdata->gchip.owner = THIS_MODULE;
+ pdata->gchip.of_xlate = tn_sn_bridge_of_xlate;
+ pdata->gchip.of_gpio_n_cells = 2;
+ pdata->gchip.free = ti_sn_bridge_gpio_free;
+ pdata->gchip.get_direction = ti_sn_bridge_gpio_get_direction;
+ pdata->gchip.direction_input = ti_sn_bridge_gpio_direction_input;
+ pdata->gchip.direction_output = ti_sn_bridge_gpio_direction_output;
+ pdata->gchip.get = ti_sn_bridge_gpio_get;
+ pdata->gchip.set = ti_sn_bridge_gpio_set;
+ pdata->gchip.can_sleep = true;
+ pdata->gchip.names = ti_sn_bridge_gpio_names;
+ pdata->gchip.ngpio = SN_NUM_GPIOS;
+ pdata->gchip.base = -1;
+ ret = devm_gpiochip_add_data(pdata->dev, &pdata->gchip, pdata);
+ if (ret)
+ dev_err(pdata->dev, "can't add gpio chip\n");
+
+ return ret;
+}
+
+#else
+
+static inline int ti_sn_setup_gpio_controller(struct ti_sn_bridge *pdata)
+{
+ return 0;
+}
+
+#endif
+
+static void ti_sn_bridge_parse_lanes(struct ti_sn_bridge *pdata,
+ struct device_node *np)
+{
+ u32 lane_assignments[SN_MAX_DP_LANES] = { 0, 1, 2, 3 };
+ u32 lane_polarities[SN_MAX_DP_LANES] = { };
+ struct device_node *endpoint;
+ u8 ln_assign = 0;
+ u8 ln_polrs = 0;
+ int dp_lanes;
+ int i;
+
+ /*
+ * Read config from the device tree about lane remapping and lane
+ * polarities. These are optional and we assume identity map and
+ * normal polarity if nothing is specified. It's OK to specify just
+ * data-lanes but not lane-polarities but not vice versa.
+ *
+ * Error checking is light (we just make sure we don't crash or
+ * buffer overrun) and we assume dts is well formed and specifying
+ * mappings that the hardware supports.
+ */
+ endpoint = of_graph_get_endpoint_by_regs(np, 1, -1);
+ dp_lanes = of_property_count_u32_elems(endpoint, "data-lanes");
+ if (dp_lanes > 0 && dp_lanes <= SN_MAX_DP_LANES) {
+ of_property_read_u32_array(endpoint, "data-lanes",
+ lane_assignments, dp_lanes);
+ of_property_read_u32_array(endpoint, "lane-polarities",
+ lane_polarities, dp_lanes);
+ } else {
+ dp_lanes = SN_MAX_DP_LANES;
+ }
+ of_node_put(endpoint);
+
+ /*
+ * Convert into register format. Loop over all lanes even if
+ * data-lanes had fewer elements so that we nicely initialize
+ * the LN_ASSIGN register.
+ */
+ for (i = SN_MAX_DP_LANES - 1; i >= 0; i--) {
+ ln_assign = ln_assign << LN_ASSIGN_WIDTH | lane_assignments[i];
+ ln_polrs = ln_polrs << 1 | lane_polarities[i];
+ }
+
+ /* Stash in our struct for when we power on */
+ pdata->dp_lanes = dp_lanes;
+ pdata->ln_assign = ln_assign;
+ pdata->ln_polrs = ln_polrs;
+}
+
static int ti_sn_bridge_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -916,6 +1201,8 @@ static int ti_sn_bridge_probe(struct i2c_client *client,
return ret;
}
+ ti_sn_bridge_parse_lanes(pdata, client->dev.of_node);
+
ret = ti_sn_bridge_parse_regulators(pdata);
if (ret) {
DRM_ERROR("failed to parse regulators\n");
@@ -937,6 +1224,12 @@ static int ti_sn_bridge_probe(struct i2c_client *client,
pm_runtime_enable(pdata->dev);
+ ret = ti_sn_setup_gpio_controller(pdata);
+ if (ret) {
+ pm_runtime_disable(pdata->dev);
+ return ret;
+ }
+
i2c_set_clientdata(client, pdata);
pdata->aux.name = "ti-sn65dsi86-aux";
diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c
index e3eb6364c0f7..ba3fa2a9b8a4 100644
--- a/drivers/gpu/drm/bridge/ti-tfp410.c
+++ b/drivers/gpu/drm/bridge/ti-tfp410.c
@@ -51,11 +51,15 @@ static int tfp410_get_modes(struct drm_connector *connector)
struct edid *edid;
int ret;
- edid = drm_bridge_get_edid(dvi->next_bridge, connector);
- if (IS_ERR_OR_NULL(edid)) {
- if (edid != ERR_PTR(-ENOTSUPP))
+ if (dvi->next_bridge->ops & DRM_BRIDGE_OP_EDID) {
+ edid = drm_bridge_get_edid(dvi->next_bridge, connector);
+ if (!edid)
DRM_INFO("EDID read failed. Fallback to standard modes\n");
+ } else {
+ edid = NULL;
+ }
+ if (!edid) {
/*
* No EDID, fallback on the XGA standard modes and prefer a mode
* pretty much anything can handle.
@@ -188,6 +192,7 @@ static void tfp410_disable(struct drm_bridge *bridge)
}
static enum drm_mode_status tfp410_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
if (mode->clock < 25000)
@@ -220,7 +225,7 @@ static int tfp410_parse_timings(struct tfp410 *dvi, bool i2c)
struct device_node *ep;
u32 pclk_sample = 0;
u32 bus_width = 24;
- s32 deskew = 0;
+ u32 deskew = 0;
/* Start with defaults. */
*timings = tfp410_default_timings;
@@ -274,12 +279,12 @@ static int tfp410_parse_timings(struct tfp410 *dvi, bool i2c)
}
/* Get the setup and hold time from vendor-specific properties. */
- of_property_read_u32(dvi->dev->of_node, "ti,deskew", (u32 *)&deskew);
- if (deskew < -4 || deskew > 3)
+ of_property_read_u32(dvi->dev->of_node, "ti,deskew", &deskew);
+ if (deskew > 7)
return -EINVAL;
- timings->setup_time_ps = min(0, 1200 - 350 * deskew);
- timings->hold_time_ps = min(0, 1300 + 350 * deskew);
+ timings->setup_time_ps = 1200 - 350 * ((s32)deskew - 4);
+ timings->hold_time_ps = max(0, 1300 + 350 * ((s32)deskew - 4));
return 0;
}