summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/bridge
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-11 12:55:44 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-11 12:55:44 -0800
commit939faf71cf7ca9ab3d1bd2912ac0e203d4d7156a (patch)
treebf81a5b8c95c7095983719a3e0273a73dbe79c34 /drivers/gpu/drm/bridge
parentb7ef56a07672e0d7ebe71c9d9b45f959f0c2f8e8 (diff)
parent2f5db9b4002470ea19380326c5a390647c56e780 (diff)
Merge tag 'drm-next-2026-02-11' of https://gitlab.freedesktop.org/drm/kernel
Pull drm updates from Dave Airlie: "Highlights: - amdgpu support for lots of new IP blocks which means newer GPUs - xe has a lot of SR-IOV and SVM improvements - lots of intel display refactoring across i915/xe - msm has more support for gen8 platforms - Given up on kgdb/kms integration, it's too hard on modern hw core: - drop kgdb support - replace system workqueue with percpu - account for property blobs in memcg - MAINTAINERS updates for xe + buddy rust: - Fix documentation for Registration constructors - Use pin_init::zeroed() for fops initialization - Annotate DRM helpers with __rust_helper - Improve safety documentation for gem::Object::new() - Update AlwaysRefCounted imports - mm: Prevent integer overflow in page_align() atomic: - add drm_device pointer to drm_private_obj - introduce gamma/degamma LUT size check buddy: - fix free_trees memory leak - prevent BUG_ON bridge: - introduce drm_bridge_unplug/enter/exit - add connector argument to .hpd_notify - lots of recounting conversions - convert rockchip inno hdmi to bridge - lontium-lt9611uxc: switch to HDMI audio helpers - dw-hdmi-qp: add support for HPD-less setups - Algoltek AG6311 support panels: - edp: CSW MNE007QB3-1, AUO B140HAN06.4, AUO B140QAX01.H - st75751: add SPI support - Sitronix ST7920, Samsung LTL106HL02 - LG LH546WF1-ED01, HannStar HSD156J - BOE NV130WUM-T08 - Innolux G150XGE-L05 - Anbernic RG-DS dma-buf: - improve sg_table debugging - add tracepoints - call clear_page instead of memset - start to introduce cgroup memory accounting in heaps - remove sysfs stats dma-fence: - add new helpers dp: - mst: avoid oob access with vcpi=0 hdmi: - limit infoframes exposure to userspace gem: - reduce page table overhead with THP - fix leak in drm_gem_get_unmapped_area gpuvm: - API sanitation for rust bindings sched: - introduce new helpers panic: - report invalid panic modes - add kunit tests i915/xe display: - Expose sharpness only if num_scalers is >= 2 - Add initial Xe3P_LPD for NVL - BMG FBC support - Add MTL+ platforms to support dpll framework _ fix DIMM_S DRM decoding on ICL - Return to using AUX interrupts - PSR/Panel replay refactoring - use consolidation HDMI tables - Xe3_LPD CD2X dividier changes xe: - vfio: add vfio_pci for intel GPU - multi queue support - dynamic pagemaps and multi-device SVM - expose temp attribs in hwmon - NO_COMPRESSION bo flag - expose MERT OA unit - sysfs survivability refactor - SRIOV PF: add MERT support - enable SR-IOV VF migration - Enable I2C/NVM on Crescent Island - Xe3p page reclaimation support - introduce SRIOV scheduler groups - add SoC remappt support in system controller - insert compiler barriers in GuC code - define NVL GuC firmware - handle GT resume failure - fix drm scheduler layering violations - enable GSC loading and PXP for PTL - disable GuC Power DCC strategy on PTL - unregister drm device on probe error i915: - move to kernel standard fault injection - bump recommended GuC version for DG2 and MTL amdgpu: - SMUIO 15.x, PSP 15.x support - IH 6.1.1/7.1 support - MMHUB 3.4/4.2 support - GC 11.5.4/12.1 support - SDMA 6.1.4/7.1/7.11.4 support - JPEG 5.3 support - UserQ updates - GC 9 gfx queue reset support - TTM memory ops parallelization - convert legacy logging to new helpers - DC analog fixes amdkfd: - GC 11.5.4/12.1 suppport - SDMA 6.1.4/7.1 support - per context support - increase kfd process hash table - Reserved SDMA rework radeon: - convert legacy logging to new helpers - use devm for i2c adapters msm: - GPU - Document a612/RGMU dt bindings - UBWC 6.0 support (for A840 / Kaanapali) - a225 support - DPU: - Switch to use virtual planes by default - Fix DSI CMD panels on DPU 3.x - Rewrite format handling to remove intermediate representation - Fix watchdog on DPU 8.x+ - Fix TE / Vsync source setting on DPU 8.x+ - Add 3D_Mux on SC7280 - Kaanapali platform support - Fix UBWC register programming - Make RM reserve DSPP-enabled mixers for CRTCs with LMs - Gamma correction support - DP: - Enable support for eDP 1.4+ link rate tables - Fix MDSS1 DP indices on SA8775P, making them to work - Fix msm_dp_ctrl_config_msa() to work with LLVM 20 - DSI: - Document QCS8300 as compatible with SA8775P - Kaanapali platform support - DSI PHY: - switch to divider_determine_rate() - MDP5: - Drop support for MSM8998, SDM660 and SDM630 (switch over to DPU) - MDSS: - Kaanapali platform support - Fixed UBWC register programming nova-core: - Prepare for Turing support. This includes parsing and handling Turing-specific firmware headers and sections as well as a Turing Falcon HAL implementation - Get rid of the Result<impl PinInit<T, E>> anti-pattern - Relocate initializer-specific code into the appropriate initializer - Use CStr::from_bytes_until_nul() to remove custom helpers - Improve handling of unexpected firmware values - Clean up redundant debug prints - Replace c_str!() with native Rust C-string literals - Update nova-core task list nova: - Align GEM object size to system page size tyr: - Use generated uAPI bindings for GpuInfo - Replace manual sleeps with read_poll_timeout() - Replace c_str!() with native Rust C-string literals - Suppress warnings for unread fields - Fix incorrect register name in print statement nouveau: - fix big page table support races in PTE management - improve reclocking on tegra 186+ amdxdna: - fix suspend race conditions - improve handling of zero tail pointers - fix cu_idx overwritten during command setup - enable hardware context priority - remove NPU2 support - update message buffer allocation requirements - update firmware version check ast: - support imported cursor buffers - big endian fixes etnaviv: - add PPU flop reset support imagination: - add AM62P support - introduce hw version checks ivpu: - implement warm boot flow panfrost: - add bo sync ioctl - add GPU_PM_RT support for RZ/G3E SoC panthor: - add bo sync ioctl - enable timestamp propagation - scheduler robustness improvements - VM termination fixes - huge page support rockchip: - RK3368 HDMI Support - get rid of atomic_check fixups - RK3506 support - RK3576/RK3588 improved HPD handling rz-du: - RZ/V2H(P) MIPI-DSI Support v3d: - fix DMA segment size - convert to new logging helpers mediatek: - move DP training to hotplug thread - convert logging to new helpers - add support for HS speed DSI - Genio 510/700/1200-EVK, Radxa NIO-12L HDMI support atmel-hlcdc: - switch to drmm resource - support nomodeset - use newer helpers hisilicon: - fix various DP bugs renesas: - fix kernel panic on reboot exynos: - fix vidi_connection_ioctl using wrong device - fix vidi_connection deref user ptr - fix concurrency regression with vidi_context vkms: - add configfs support for display configuration * tag 'drm-next-2026-02-11' of https://gitlab.freedesktop.org/drm/kernel: (1610 commits) drm/xe/pm: Disable D3Cold for BMG only on specific platforms drm/xe: Fix kerneldoc for xe_tlb_inval_job_alloc_dep drm/xe: Fix kerneldoc for xe_gt_tlb_inval_init_early drm/xe: Fix kerneldoc for xe_migrate_exec_queue drm/xe/query: Fix topology query pointer advance drm/xe/guc: Fix kernel-doc warning in GuC scheduler ABI header drm/xe/guc: Fix CFI violation in debugfs access. accel/amdxdna: Move RPM resume into job run function accel/amdxdna: Fix incorrect DPM level after suspend/resume nouveau/vmm: start tracking if the LPT PTE is valid. (v6) nouveau/vmm: increase size of vmm pte tracker struct to u32 (v2) nouveau/vmm: rewrite pte tracker using a struct and bitfields. accel/amdxdna: Fix incorrect error code returned for failed chain command accel/amdxdna: Remove hardware context status drm/bridge: imx8qxp-pixel-combiner: Fix bailout for imx8qxp_pc_bridge_probe() drm/panel: ilitek-ili9882t: Remove duplicate initializers in tianma_il79900a_dsc drm/i915/display: fix the pixel normalization handling for xe3p_lpd drm/exynos: vidi: use ctx->lock to protect struct vidi_context member variables related to memory alloc/free drm/exynos: vidi: fix to avoid directly dereferencing user pointer drm/exynos: vidi: use priv->vidi_dev for ctx lookup in vidi_connection_ioctl() ...
Diffstat (limited to 'drivers/gpu/drm/bridge')
-rw-r--r--drivers/gpu/drm/bridge/Kconfig7
-rw-r--r--drivers/gpu/drm/bridge/Makefile1
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7511_drv.c180
-rw-r--r--drivers/gpu/drm/bridge/analogix/anx7625.c2
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c15
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c15
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c9
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c9
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c66
-rw-r--r--drivers/gpu/drm/bridge/inno-hdmi.c1143
-rw-r--r--drivers/gpu/drm/bridge/ite-it6263.c95
-rw-r--r--drivers/gpu/drm/bridge/ite-it66121.c7
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt8912b.c31
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9611.c143
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9611uxc.c125
-rw-r--r--drivers/gpu/drm/bridge/samsung-dsim.c37
-rw-r--r--drivers/gpu/drm/bridge/sii902x.c7
-rw-r--r--drivers/gpu/drm/bridge/simple-bridge.c20
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c142
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.c7
-rw-r--r--drivers/gpu/drm/bridge/thc63lvd1024.c7
-rw-r--r--drivers/gpu/drm/bridge/ti-sn65dsi83.c86
-rw-r--r--drivers/gpu/drm/bridge/ti-tfp410.c27
-rw-r--r--drivers/gpu/drm/bridge/ti-tpd12s015.c8
24 files changed, 1741 insertions, 448 deletions
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index a250afd8d662..39385deafc68 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -100,6 +100,13 @@ config DRM_I2C_NXP_TDA998X
help
Support for NXP Semiconductors TDA998X HDMI encoders.
+config DRM_INNO_HDMI
+ tristate
+ select DRM_BRIDGE_CONNECTOR
+ select DRM_DISPLAY_HDMI_HELPER
+ select DRM_DISPLAY_HELPER
+ select DRM_KMS_HELPER
+
config DRM_ITE_IT6263
tristate "ITE IT6263 LVDS/HDMI bridge"
depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index c7dc03182e59..909c21cc3acd 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_DRM_FSL_LDB) += fsl-ldb.o
tda998x-y := tda998x_drv.o
obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
+obj-$(CONFIG_DRM_INNO_HDMI) += inno-hdmi.o
obj-$(CONFIG_DRM_ITE_IT6263) += ite-it6263.o
obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
index b9be86541307..1050bb62280b 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
@@ -887,88 +887,111 @@ static const struct drm_edid *adv7511_bridge_edid_read(struct drm_bridge *bridge
return adv7511_edid_read(adv, connector);
}
-static int adv7511_bridge_hdmi_clear_infoframe(struct drm_bridge *bridge,
- enum hdmi_infoframe_type type)
+static int adv7511_bridge_hdmi_clear_audio_infoframe(struct drm_bridge *bridge)
{
struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
- switch (type) {
- case HDMI_INFOFRAME_TYPE_AUDIO:
- adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME);
- break;
- case HDMI_INFOFRAME_TYPE_AVI:
- adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME);
- break;
- case HDMI_INFOFRAME_TYPE_SPD:
- adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPD);
- break;
- case HDMI_INFOFRAME_TYPE_VENDOR:
- adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPARE1);
- break;
- default:
- drm_dbg_driver(adv7511->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type);
- break;
- }
+ adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME);
return 0;
}
-static int adv7511_bridge_hdmi_write_infoframe(struct drm_bridge *bridge,
- enum hdmi_infoframe_type type,
- const u8 *buffer, size_t len)
+static int adv7511_bridge_hdmi_clear_avi_infoframe(struct drm_bridge *bridge)
{
struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
- switch (type) {
- case HDMI_INFOFRAME_TYPE_AUDIO:
- /* send current Audio infoframe values while updating */
- regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
- BIT(5), BIT(5));
-
- /* The Audio infoframe id is not configurable */
- regmap_bulk_write(adv7511->regmap, ADV7511_REG_AUDIO_INFOFRAME_VERSION,
- buffer + 1, len - 1);
-
- /* use Audio infoframe updated info */
- regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
- BIT(5), 0);
-
- adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME);
- break;
- case HDMI_INFOFRAME_TYPE_AVI:
- /* send current AVI infoframe values while updating */
- regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
- BIT(6), BIT(6));
-
- /* The AVI infoframe id is not configurable */
- regmap_bulk_write(adv7511->regmap, ADV7511_REG_AVI_INFOFRAME_VERSION,
- buffer + 1, len - 1);
-
- regmap_write(adv7511->regmap, ADV7511_REG_AUDIO_INFOFRAME_LENGTH, 0x2);
- regmap_write(adv7511->regmap, ADV7511_REG_AUDIO_INFOFRAME(1), 0x1);
-
- /* use AVI infoframe updated info */
- regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
- BIT(6), 0);
-
- adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME);
- break;
- case HDMI_INFOFRAME_TYPE_SPD:
- adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPD);
- regmap_bulk_write(adv7511->regmap_packet, ADV7511_PACKET_SPD(0),
- buffer, len);
- adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_SPD);
- break;
- case HDMI_INFOFRAME_TYPE_VENDOR:
- adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPARE1);
- regmap_bulk_write(adv7511->regmap_packet, ADV7511_PACKET_SPARE1(0),
- buffer, len);
- adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_SPARE1);
- break;
- default:
- drm_dbg_driver(adv7511->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type);
- break;
- }
+ adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME);
+
+ return 0;
+}
+
+static int adv7511_bridge_hdmi_clear_spd_infoframe(struct drm_bridge *bridge)
+{
+ struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
+
+ adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPD);
+
+ return 0;
+}
+
+static int adv7511_bridge_hdmi_clear_hdmi_infoframe(struct drm_bridge *bridge)
+{
+ struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
+
+ adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPARE1);
+
+ return 0;
+}
+
+static int adv7511_bridge_hdmi_write_audio_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
+
+ /* send current Audio infoframe values while updating */
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
+ BIT(5), BIT(5));
+
+ /* The Audio infoframe id is not configurable */
+ regmap_bulk_write(adv7511->regmap, ADV7511_REG_AUDIO_INFOFRAME_VERSION,
+ buffer + 1, len - 1);
+
+ /* use Audio infoframe updated info */
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
+ BIT(5), 0);
+
+ adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME);
+
+ return 0;
+}
+
+static int adv7511_bridge_hdmi_write_avi_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
+
+ /* send current AVI infoframe values while updating */
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
+ BIT(6), BIT(6));
+
+ /* The AVI infoframe id is not configurable */
+ regmap_bulk_write(adv7511->regmap, ADV7511_REG_AVI_INFOFRAME_VERSION,
+ buffer + 1, len - 1);
+
+ regmap_write(adv7511->regmap, ADV7511_REG_AUDIO_INFOFRAME_LENGTH, 0x2);
+ regmap_write(adv7511->regmap, ADV7511_REG_AUDIO_INFOFRAME(1), 0x1);
+
+ /* use AVI infoframe updated info */
+ regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
+ BIT(6), 0);
+
+ adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME);
+
+ return 0;
+}
+
+static int adv7511_bridge_hdmi_write_spd_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
+
+ adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPD);
+ regmap_bulk_write(adv7511->regmap_packet, ADV7511_PACKET_SPD(0),
+ buffer, len);
+ adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_SPD);
+
+ return 0;
+}
+
+static int adv7511_bridge_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct adv7511 *adv7511 = bridge_to_adv7511(bridge);
+
+ adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPARE1);
+ regmap_bulk_write(adv7511->regmap_packet, ADV7511_PACKET_SPARE1(0),
+ buffer, len);
+ adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_SPARE1);
return 0;
}
@@ -986,8 +1009,14 @@ static const struct drm_bridge_funcs adv7511_bridge_funcs = {
.atomic_reset = drm_atomic_helper_bridge_reset,
.hdmi_tmds_char_rate_valid = adv7511_bridge_hdmi_tmds_char_rate_valid,
- .hdmi_clear_infoframe = adv7511_bridge_hdmi_clear_infoframe,
- .hdmi_write_infoframe = adv7511_bridge_hdmi_write_infoframe,
+ .hdmi_clear_audio_infoframe = adv7511_bridge_hdmi_clear_audio_infoframe,
+ .hdmi_write_audio_infoframe = adv7511_bridge_hdmi_write_audio_infoframe,
+ .hdmi_clear_avi_infoframe = adv7511_bridge_hdmi_clear_avi_infoframe,
+ .hdmi_write_avi_infoframe = adv7511_bridge_hdmi_write_avi_infoframe,
+ .hdmi_clear_spd_infoframe = adv7511_bridge_hdmi_clear_spd_infoframe,
+ .hdmi_write_spd_infoframe = adv7511_bridge_hdmi_write_spd_infoframe,
+ .hdmi_clear_hdmi_infoframe = adv7511_bridge_hdmi_clear_hdmi_infoframe,
+ .hdmi_write_hdmi_infoframe = adv7511_bridge_hdmi_write_hdmi_infoframe,
.hdmi_audio_startup = adv7511_hdmi_audio_startup,
.hdmi_audio_prepare = adv7511_hdmi_audio_prepare,
@@ -1322,7 +1351,8 @@ static int adv7511_probe(struct i2c_client *i2c)
adv7511->bridge.ops = DRM_BRIDGE_OP_DETECT |
DRM_BRIDGE_OP_EDID |
- DRM_BRIDGE_OP_HDMI;
+ DRM_BRIDGE_OP_HDMI |
+ DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME;
if (adv7511->i2c_main->irq)
adv7511->bridge.ops |= DRM_BRIDGE_OP_HPD;
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
index 6f3fdcb6afdb..4e49e4f28d55 100644
--- a/drivers/gpu/drm/bridge/analogix/anx7625.c
+++ b/drivers/gpu/drm/bridge/analogix/anx7625.c
@@ -1801,7 +1801,7 @@ static const struct drm_edid *anx7625_edid_read(struct anx7625_data *ctx)
return NULL;
}
- ctx->cached_drm_edid = drm_edid_alloc(edid_buf, FOUR_BLOCK_SIZE);
+ ctx->cached_drm_edid = drm_edid_alloc(edid_buf, edid_num * ONE_BLOCK_SIZE);
kfree(edid_buf);
out:
diff --git a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c
index 3a6f8587a257..15fbb1be07cd 100644
--- a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c
+++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c
@@ -29,7 +29,6 @@
struct imx8mp_hdmi_pvi {
struct drm_bridge bridge;
struct device *dev;
- struct drm_bridge *next_bridge;
void __iomem *regs;
};
@@ -45,7 +44,7 @@ static int imx8mp_hdmi_pvi_bridge_attach(struct drm_bridge *bridge,
{
struct imx8mp_hdmi_pvi *pvi = to_imx8mp_hdmi_pvi(bridge);
- return drm_bridge_attach(encoder, pvi->next_bridge,
+ return drm_bridge_attach(encoder, pvi->bridge.next_bridge,
bridge, flags);
}
@@ -78,8 +77,8 @@ static void imx8mp_hdmi_pvi_bridge_enable(struct drm_bridge *bridge,
if (mode->flags & DRM_MODE_FLAG_PHSYNC)
val |= PVI_CTRL_OP_HSYNC_POL | PVI_CTRL_INP_HSYNC_POL;
- if (pvi->next_bridge->timings)
- bus_flags = pvi->next_bridge->timings->input_bus_flags;
+ if (pvi->bridge.next_bridge->timings)
+ bus_flags = pvi->bridge.next_bridge->timings->input_bus_flags;
else if (bridge_state)
bus_flags = bridge_state->input_bus_cfg.flags;
@@ -108,7 +107,7 @@ imx8mp_hdmi_pvi_bridge_get_input_bus_fmts(struct drm_bridge *bridge,
unsigned int *num_input_fmts)
{
struct imx8mp_hdmi_pvi *pvi = to_imx8mp_hdmi_pvi(bridge);
- struct drm_bridge *next_bridge = pvi->next_bridge;
+ struct drm_bridge *next_bridge = pvi->bridge.next_bridge;
struct drm_bridge_state *next_state;
if (!next_bridge->funcs->atomic_get_input_bus_fmts)
@@ -157,10 +156,10 @@ static int imx8mp_hdmi_pvi_probe(struct platform_device *pdev)
if (!remote)
return -EINVAL;
- pvi->next_bridge = of_drm_find_bridge(remote);
+ pvi->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
of_node_put(remote);
- if (!pvi->next_bridge)
+ if (!pvi->bridge.next_bridge)
return dev_err_probe(&pdev->dev, -EPROBE_DEFER,
"could not find next bridge\n");
@@ -168,7 +167,7 @@ static int imx8mp_hdmi_pvi_probe(struct platform_device *pdev)
/* Register the bridge. */
pvi->bridge.of_node = pdev->dev.of_node;
- pvi->bridge.timings = pvi->next_bridge->timings;
+ pvi->bridge.timings = pvi->bridge.next_bridge->timings;
drm_bridge_add(&pvi->bridge);
diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c b/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c
index d70f3c9b3925..ada11eed13cf 100644
--- a/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c
+++ b/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c
@@ -62,6 +62,18 @@ static inline struct imx8qxp_ldb *base_to_imx8qxp_ldb(struct ldb *base)
return container_of(base, struct imx8qxp_ldb, base);
}
+static void imx8qxp_ldb_bridge_destroy(struct drm_bridge *bridge)
+{
+ struct ldb_channel *ldb_ch = bridge->driver_private;
+ struct imx8qxp_ldb *imx8qxp_ldb;
+
+ if (!ldb_ch)
+ return;
+
+ imx8qxp_ldb = base_to_imx8qxp_ldb(ldb_ch->ldb);
+ drm_bridge_put(imx8qxp_ldb->companion);
+}
+
static void imx8qxp_ldb_set_phy_cfg(struct imx8qxp_ldb *imx8qxp_ldb,
unsigned long di_clk, bool is_split,
struct phy_configure_opts_lvds *phy_cfg)
@@ -389,6 +401,7 @@ imx8qxp_ldb_bridge_mode_valid(struct drm_bridge *bridge,
}
static const struct drm_bridge_funcs imx8qxp_ldb_bridge_funcs = {
+ .destroy = imx8qxp_ldb_bridge_destroy,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
@@ -550,7 +563,7 @@ static int imx8qxp_ldb_parse_dt_companion(struct imx8qxp_ldb *imx8qxp_ldb)
goto out;
}
- imx8qxp_ldb->companion = of_drm_find_bridge(companion_port);
+ imx8qxp_ldb->companion = of_drm_find_and_get_bridge(companion_port);
if (!imx8qxp_ldb->companion) {
ret = -EPROBE_DEFER;
DRM_DEV_DEBUG_DRIVER(dev,
diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c
index 8e64b5404561..27ad66f240cf 100644
--- a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c
+++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c
@@ -60,7 +60,6 @@ enum imx8qxp_pc_pix_data_format {
struct imx8qxp_pc_channel {
struct drm_bridge bridge;
- struct drm_bridge *next_bridge;
struct imx8qxp_pc *pc;
unsigned int stream_id;
};
@@ -120,7 +119,7 @@ static int imx8qxp_pc_bridge_attach(struct drm_bridge *bridge,
}
return drm_bridge_attach(encoder,
- ch->next_bridge, bridge,
+ ch->bridge.next_bridge, bridge,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
}
@@ -323,8 +322,8 @@ static int imx8qxp_pc_bridge_probe(struct platform_device *pdev)
goto free_child;
}
- ch->next_bridge = of_drm_find_bridge(remote);
- if (!ch->next_bridge) {
+ ch->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
+ if (!ch->bridge.next_bridge) {
of_node_put(remote);
ret = -EPROBE_DEFER;
DRM_DEV_DEBUG_DRIVER(dev,
@@ -346,7 +345,7 @@ static int imx8qxp_pc_bridge_probe(struct platform_device *pdev)
free_child:
of_node_put(child);
- if (i == 1 && pc->ch[0]->next_bridge)
+ if (i == 1 && pc->ch[0] && pc->ch[0]->bridge.next_bridge)
drm_bridge_remove(&pc->ch[0]->bridge);
pm_runtime_disable(dev);
diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c
index e5943506981d..433c080197a2 100644
--- a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c
+++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c
@@ -374,13 +374,8 @@ static int imx8qxp_pixel_link_bridge_probe(struct platform_device *pdev)
return ret;
pl->next_bridge = imx8qxp_pixel_link_find_next_bridge(pl);
- if (IS_ERR(pl->next_bridge)) {
- ret = PTR_ERR(pl->next_bridge);
- if (ret != -EPROBE_DEFER)
- DRM_DEV_ERROR(dev, "failed to find next bridge: %d\n",
- ret);
- return ret;
- }
+ if (IS_ERR(pl->next_bridge))
+ return PTR_ERR(pl->next_bridge);
platform_set_drvdata(pdev, pl);
diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c
index 82a2bba375ad..9dc2b3d2ecef 100644
--- a/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c
+++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c
@@ -35,7 +35,6 @@
struct imx8qxp_pxl2dpi {
struct regmap *regmap;
struct drm_bridge bridge;
- struct drm_bridge *next_bridge;
struct drm_bridge *companion;
struct device *dev;
struct imx_sc_ipc *ipc_handle;
@@ -60,10 +59,20 @@ static int imx8qxp_pxl2dpi_bridge_attach(struct drm_bridge *bridge,
}
return drm_bridge_attach(encoder,
- p2d->next_bridge, bridge,
+ p2d->bridge.next_bridge, bridge,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
}
+static void imx8qxp_pxl2dpi_bridge_destroy(struct drm_bridge *bridge)
+{
+ struct imx8qxp_pxl2dpi *p2d = bridge->driver_private;
+
+ if (!p2d)
+ return;
+
+ drm_bridge_put(p2d->companion);
+}
+
static int
imx8qxp_pxl2dpi_bridge_atomic_check(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
@@ -203,6 +212,7 @@ static const struct drm_bridge_funcs imx8qxp_pxl2dpi_bridge_funcs = {
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
.attach = imx8qxp_pxl2dpi_bridge_attach,
+ .destroy = imx8qxp_pxl2dpi_bridge_destroy,
.atomic_check = imx8qxp_pxl2dpi_bridge_atomic_check,
.mode_set = imx8qxp_pxl2dpi_bridge_mode_set,
.atomic_disable = imx8qxp_pxl2dpi_bridge_atomic_disable,
@@ -252,40 +262,27 @@ out:
return ep;
}
-static struct drm_bridge *
-imx8qxp_pxl2dpi_find_next_bridge(struct imx8qxp_pxl2dpi *p2d)
+static int imx8qxp_pxl2dpi_find_next_bridge(struct imx8qxp_pxl2dpi *p2d)
{
- struct device_node *ep, *remote;
- struct drm_bridge *next_bridge;
- int ret;
-
- ep = imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 1);
- if (IS_ERR(ep)) {
- ret = PTR_ERR(ep);
- return ERR_PTR(ret);
- }
+ struct device_node *ep __free(device_node) =
+ imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 1);
+ if (IS_ERR(ep))
+ return PTR_ERR(ep);
- remote = of_graph_get_remote_port_parent(ep);
+ struct device_node *remote __free(device_node) = of_graph_get_remote_port_parent(ep);
if (!remote || !of_device_is_available(remote)) {
DRM_DEV_ERROR(p2d->dev, "no available remote\n");
- next_bridge = ERR_PTR(-ENODEV);
- goto out;
+ return -ENODEV;
} else if (!of_device_is_available(remote->parent)) {
DRM_DEV_ERROR(p2d->dev, "remote parent is not available\n");
- next_bridge = ERR_PTR(-ENODEV);
- goto out;
+ return -ENODEV;
}
- next_bridge = of_drm_find_bridge(remote);
- if (!next_bridge) {
- next_bridge = ERR_PTR(-EPROBE_DEFER);
- goto out;
- }
-out:
- of_node_put(remote);
- of_node_put(ep);
+ p2d->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
+ if (!p2d->bridge.next_bridge)
+ return -EPROBE_DEFER;
- return next_bridge;
+ return 0;
}
static int imx8qxp_pxl2dpi_set_pixel_link_sel(struct imx8qxp_pxl2dpi *p2d)
@@ -344,7 +341,7 @@ static int imx8qxp_pxl2dpi_parse_dt_companion(struct imx8qxp_pxl2dpi *p2d)
goto out;
}
- p2d->companion = of_drm_find_bridge(companion);
+ p2d->companion = of_drm_find_and_get_bridge(companion);
if (!p2d->companion) {
ret = -EPROBE_DEFER;
DRM_DEV_DEBUG_DRIVER(p2d->dev,
@@ -361,8 +358,8 @@ static int imx8qxp_pxl2dpi_parse_dt_companion(struct imx8qxp_pxl2dpi *p2d)
* the next bridges are connected to. If they are marked as expecting
* even pixels and odd pixels than we need to use the companion PXL2DPI.
*/
- port1 = of_graph_get_port_by_id(p2d->next_bridge->of_node, 1);
- port2 = of_graph_get_port_by_id(companion_p2d->next_bridge->of_node, 1);
+ port1 = of_graph_get_port_by_id(p2d->bridge.next_bridge->of_node, 1);
+ port2 = of_graph_get_port_by_id(companion_p2d->bridge.next_bridge->of_node, 1);
dual_link = drm_of_lvds_get_dual_link_pixel_order(port1, port2);
of_node_put(port1);
of_node_put(port2);
@@ -418,14 +415,9 @@ static int imx8qxp_pxl2dpi_bridge_probe(struct platform_device *pdev)
return ret;
}
- p2d->next_bridge = imx8qxp_pxl2dpi_find_next_bridge(p2d);
- if (IS_ERR(p2d->next_bridge)) {
- ret = PTR_ERR(p2d->next_bridge);
- if (ret != -EPROBE_DEFER)
- DRM_DEV_ERROR(dev, "failed to find next bridge: %d\n",
- ret);
+ ret = imx8qxp_pxl2dpi_find_next_bridge(p2d);
+ if (ret)
return ret;
- }
ret = imx8qxp_pxl2dpi_set_pixel_link_sel(p2d);
if (ret)
diff --git a/drivers/gpu/drm/bridge/inno-hdmi.c b/drivers/gpu/drm/bridge/inno-hdmi.c
new file mode 100644
index 000000000000..a26b99b101c4
--- /dev/null
+++ b/drivers/gpu/drm/bridge/inno-hdmi.c
@@ -0,0 +1,1143 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) Rockchip Electronics Co., Ltd.
+ * Zheng Yang <zhengyang@rock-chips.com>
+ * Yakir Yang <ykk@rock-chips.com>
+ * Andy Yan <andyshrk@163.com>
+ */
+
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/hdmi.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <drm/bridge/inno_hdmi.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include <drm/display/drm_hdmi_helper.h>
+#include <drm/display/drm_hdmi_state_helper.h>
+
+#define INNO_HDMI_MIN_TMDS_CLOCK 25000000U
+
+#define DDC_SEGMENT_ADDR 0x30
+
+#define HDMI_SCL_RATE (100 * 1000)
+
+#define DDC_BUS_FREQ_L 0x4b
+#define DDC_BUS_FREQ_H 0x4c
+
+#define HDMI_SYS_CTRL 0x00
+#define m_RST_ANALOG BIT(6)
+#define v_RST_ANALOG (0 << 6)
+#define v_NOT_RST_ANALOG BIT(6)
+#define m_RST_DIGITAL BIT(5)
+#define v_RST_DIGITAL (0 << 5)
+#define v_NOT_RST_DIGITAL BIT(5)
+#define m_REG_CLK_INV BIT(4)
+#define v_REG_CLK_NOT_INV (0 << 4)
+#define v_REG_CLK_INV BIT(4)
+#define m_VCLK_INV BIT(3)
+#define v_VCLK_NOT_INV (0 << 3)
+#define v_VCLK_INV BIT(3)
+#define m_REG_CLK_SOURCE BIT(2)
+#define v_REG_CLK_SOURCE_TMDS (0 << 2)
+#define v_REG_CLK_SOURCE_SYS BIT(2)
+#define m_POWER BIT(1)
+#define v_PWR_ON (0 << 1)
+#define v_PWR_OFF BIT(1)
+#define m_INT_POL BIT(0)
+#define v_INT_POL_HIGH 1
+#define v_INT_POL_LOW 0
+
+#define HDMI_VIDEO_CONTRL1 0x01
+#define m_VIDEO_INPUT_FORMAT (7 << 1)
+#define m_DE_SOURCE BIT(0)
+#define v_VIDEO_INPUT_FORMAT(n) ((n) << 1)
+#define v_DE_EXTERNAL 1
+#define v_DE_INTERNAL 0
+enum {
+ VIDEO_INPUT_SDR_RGB444 = 0,
+ VIDEO_INPUT_DDR_RGB444 = 5,
+ VIDEO_INPUT_DDR_YCBCR422 = 6
+};
+
+#define HDMI_VIDEO_CONTRL2 0x02
+#define m_VIDEO_OUTPUT_COLOR (3 << 6)
+#define m_VIDEO_INPUT_BITS (3 << 4)
+#define m_VIDEO_INPUT_CSP BIT(0)
+#define v_VIDEO_OUTPUT_COLOR(n) (((n) & 0x3) << 6)
+#define v_VIDEO_INPUT_BITS(n) ((n) << 4)
+#define v_VIDEO_INPUT_CSP(n) ((n) << 0)
+enum {
+ VIDEO_INPUT_12BITS = 0,
+ VIDEO_INPUT_10BITS = 1,
+ VIDEO_INPUT_REVERT = 2,
+ VIDEO_INPUT_8BITS = 3,
+};
+
+#define HDMI_VIDEO_CONTRL 0x03
+#define m_VIDEO_AUTO_CSC BIT(7)
+#define v_VIDEO_AUTO_CSC(n) ((n) << 7)
+#define m_VIDEO_C0_C2_SWAP BIT(0)
+#define v_VIDEO_C0_C2_SWAP(n) ((n) << 0)
+enum {
+ C0_C2_CHANGE_ENABLE = 0,
+ C0_C2_CHANGE_DISABLE = 1,
+ AUTO_CSC_DISABLE = 0,
+ AUTO_CSC_ENABLE = 1,
+};
+
+#define HDMI_VIDEO_CONTRL3 0x04
+#define m_COLOR_DEPTH_NOT_INDICATED BIT(4)
+#define m_SOF BIT(3)
+#define m_COLOR_RANGE BIT(2)
+#define m_CSC BIT(0)
+#define v_COLOR_DEPTH_NOT_INDICATED(n) ((n) << 4)
+#define v_SOF_ENABLE (0 << 3)
+#define v_SOF_DISABLE BIT(3)
+#define v_COLOR_RANGE_FULL BIT(2)
+#define v_COLOR_RANGE_LIMITED (0 << 2)
+#define v_CSC_ENABLE 1
+#define v_CSC_DISABLE 0
+
+#define HDMI_AV_MUTE 0x05
+#define m_AVMUTE_CLEAR BIT(7)
+#define m_AVMUTE_ENABLE BIT(6)
+#define m_AUDIO_MUTE BIT(1)
+#define m_VIDEO_BLACK BIT(0)
+#define v_AVMUTE_CLEAR(n) ((n) << 7)
+#define v_AVMUTE_ENABLE(n) ((n) << 6)
+#define v_AUDIO_MUTE(n) ((n) << 1)
+#define v_VIDEO_MUTE(n) ((n) << 0)
+
+#define HDMI_VIDEO_TIMING_CTL 0x08
+#define v_HSYNC_POLARITY(n) ((n) << 3)
+#define v_VSYNC_POLARITY(n) ((n) << 2)
+#define v_INETLACE(n) ((n) << 1)
+#define v_EXTERANL_VIDEO(n) ((n) << 0)
+
+#define HDMI_VIDEO_EXT_HTOTAL_L 0x09
+#define HDMI_VIDEO_EXT_HTOTAL_H 0x0a
+#define HDMI_VIDEO_EXT_HBLANK_L 0x0b
+#define HDMI_VIDEO_EXT_HBLANK_H 0x0c
+#define HDMI_VIDEO_EXT_HDELAY_L 0x0d
+#define HDMI_VIDEO_EXT_HDELAY_H 0x0e
+#define HDMI_VIDEO_EXT_HDURATION_L 0x0f
+#define HDMI_VIDEO_EXT_HDURATION_H 0x10
+#define HDMI_VIDEO_EXT_VTOTAL_L 0x11
+#define HDMI_VIDEO_EXT_VTOTAL_H 0x12
+#define HDMI_VIDEO_EXT_VBLANK 0x13
+#define HDMI_VIDEO_EXT_VDELAY 0x14
+#define HDMI_VIDEO_EXT_VDURATION 0x15
+
+#define HDMI_VIDEO_CSC_COEF 0x18
+
+#define HDMI_AUDIO_CTRL1 0x35
+enum {
+ CTS_SOURCE_INTERNAL = 0,
+ CTS_SOURCE_EXTERNAL = 1,
+};
+
+#define v_CTS_SOURCE(n) ((n) << 7)
+
+enum {
+ DOWNSAMPLE_DISABLE = 0,
+ DOWNSAMPLE_1_2 = 1,
+ DOWNSAMPLE_1_4 = 2,
+};
+
+#define v_DOWN_SAMPLE(n) ((n) << 5)
+
+enum {
+ AUDIO_SOURCE_IIS = 0,
+ AUDIO_SOURCE_SPDIF = 1,
+};
+
+#define v_AUDIO_SOURCE(n) ((n) << 3)
+
+#define v_MCLK_ENABLE(n) ((n) << 2)
+
+enum {
+ MCLK_128FS = 0,
+ MCLK_256FS = 1,
+ MCLK_384FS = 2,
+ MCLK_512FS = 3,
+};
+
+#define v_MCLK_RATIO(n) (n)
+
+#define AUDIO_SAMPLE_RATE 0x37
+
+enum {
+ AUDIO_32K = 0x3,
+ AUDIO_441K = 0x0,
+ AUDIO_48K = 0x2,
+ AUDIO_882K = 0x8,
+ AUDIO_96K = 0xa,
+ AUDIO_1764K = 0xc,
+ AUDIO_192K = 0xe,
+};
+
+#define AUDIO_I2S_MODE 0x38
+
+enum {
+ I2S_CHANNEL_1_2 = 1,
+ I2S_CHANNEL_3_4 = 3,
+ I2S_CHANNEL_5_6 = 7,
+ I2S_CHANNEL_7_8 = 0xf
+};
+
+#define v_I2S_CHANNEL(n) ((n) << 2)
+
+enum {
+ I2S_STANDARD = 0,
+ I2S_LEFT_JUSTIFIED = 1,
+ I2S_RIGHT_JUSTIFIED = 2,
+};
+
+#define v_I2S_MODE(n) (n)
+
+#define AUDIO_I2S_MAP 0x39
+#define AUDIO_I2S_SWAPS_SPDIF 0x3a
+#define v_SPIDF_FREQ(n) (n)
+
+#define N_32K 0x1000
+#define N_441K 0x1880
+#define N_882K 0x3100
+#define N_1764K 0x6200
+#define N_48K 0x1800
+#define N_96K 0x3000
+#define N_192K 0x6000
+
+#define HDMI_AUDIO_CHANNEL_STATUS 0x3e
+#define m_AUDIO_STATUS_NLPCM BIT(7)
+#define m_AUDIO_STATUS_USE BIT(6)
+#define m_AUDIO_STATUS_COPYRIGHT BIT(5)
+#define m_AUDIO_STATUS_ADDITION (3 << 2)
+#define m_AUDIO_STATUS_CLK_ACCURACY (2 << 0)
+#define v_AUDIO_STATUS_NLPCM(n) (((n) & 1) << 7)
+#define AUDIO_N_H 0x3f
+#define AUDIO_N_M 0x40
+#define AUDIO_N_L 0x41
+
+#define HDMI_AUDIO_CTS_H 0x45
+#define HDMI_AUDIO_CTS_M 0x46
+#define HDMI_AUDIO_CTS_L 0x47
+
+#define HDMI_DDC_CLK_L 0x4b
+#define HDMI_DDC_CLK_H 0x4c
+
+#define HDMI_EDID_SEGMENT_POINTER 0x4d
+#define HDMI_EDID_WORD_ADDR 0x4e
+#define HDMI_EDID_FIFO_OFFSET 0x4f
+#define HDMI_EDID_FIFO_ADDR 0x50
+
+#define HDMI_PACKET_SEND_MANUAL 0x9c
+#define HDMI_PACKET_SEND_AUTO 0x9d
+#define m_PACKET_GCP_EN BIT(7)
+#define m_PACKET_MSI_EN BIT(6)
+#define m_PACKET_SDI_EN BIT(5)
+#define m_PACKET_VSI_EN BIT(4)
+#define v_PACKET_GCP_EN(n) (((n) & 1) << 7)
+#define v_PACKET_MSI_EN(n) (((n) & 1) << 6)
+#define v_PACKET_SDI_EN(n) (((n) & 1) << 5)
+#define v_PACKET_VSI_EN(n) (((n) & 1) << 4)
+
+#define HDMI_CONTROL_PACKET_BUF_INDEX 0x9f
+
+enum {
+ INFOFRAME_VSI = 0x05,
+ INFOFRAME_AVI = 0x06,
+ INFOFRAME_AAI = 0x08,
+};
+
+#define HDMI_CONTROL_PACKET_ADDR 0xa0
+#define HDMI_MAXIMUM_INFO_FRAME_SIZE 0x11
+
+enum {
+ AVI_COLOR_MODE_RGB = 0,
+ AVI_COLOR_MODE_YCBCR422 = 1,
+ AVI_COLOR_MODE_YCBCR444 = 2,
+ AVI_COLORIMETRY_NO_DATA = 0,
+
+ AVI_COLORIMETRY_SMPTE_170M = 1,
+ AVI_COLORIMETRY_ITU709 = 2,
+ AVI_COLORIMETRY_EXTENDED = 3,
+
+ AVI_CODED_FRAME_ASPECT_NO_DATA = 0,
+ AVI_CODED_FRAME_ASPECT_4_3 = 1,
+ AVI_CODED_FRAME_ASPECT_16_9 = 2,
+
+ ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08,
+ ACTIVE_ASPECT_RATE_4_3 = 0x09,
+ ACTIVE_ASPECT_RATE_16_9 = 0x0A,
+ ACTIVE_ASPECT_RATE_14_9 = 0x0B,
+};
+
+#define HDMI_HDCP_CTRL 0x52
+#define m_HDMI_DVI BIT(1)
+#define v_HDMI_DVI(n) ((n) << 1)
+
+#define HDMI_INTERRUPT_MASK1 0xc0
+#define HDMI_INTERRUPT_STATUS1 0xc1
+#define m_INT_ACTIVE_VSYNC BIT(5)
+#define m_INT_EDID_READY BIT(2)
+
+#define HDMI_INTERRUPT_MASK2 0xc2
+#define HDMI_INTERRUPT_STATUS2 0xc3
+#define m_INT_HDCP_ERR BIT(7)
+#define m_INT_BKSV_FLAG BIT(6)
+#define m_INT_HDCP_OK BIT(4)
+
+#define HDMI_STATUS 0xc8
+#define m_HOTPLUG BIT(7)
+#define m_MASK_INT_HOTPLUG BIT(5)
+#define m_INT_HOTPLUG BIT(1)
+#define v_MASK_INT_HOTPLUG(n) (((n) & 0x1) << 5)
+
+#define HDMI_COLORBAR 0xc9
+
+#define HDMI_PHY_SYNC 0xce
+#define HDMI_PHY_SYS_CTL 0xe0
+#define m_TMDS_CLK_SOURCE BIT(5)
+#define v_TMDS_FROM_PLL (0 << 5)
+#define v_TMDS_FROM_GEN BIT(5)
+#define m_PHASE_CLK BIT(4)
+#define v_DEFAULT_PHASE (0 << 4)
+#define v_SYNC_PHASE BIT(4)
+#define m_TMDS_CURRENT_PWR BIT(3)
+#define v_TURN_ON_CURRENT (0 << 3)
+#define v_CAT_OFF_CURRENT BIT(3)
+#define m_BANDGAP_PWR BIT(2)
+#define v_BANDGAP_PWR_UP (0 << 2)
+#define v_BANDGAP_PWR_DOWN BIT(2)
+#define m_PLL_PWR BIT(1)
+#define v_PLL_PWR_UP (0 << 1)
+#define v_PLL_PWR_DOWN BIT(1)
+#define m_TMDS_CHG_PWR BIT(0)
+#define v_TMDS_CHG_PWR_UP (0 << 0)
+#define v_TMDS_CHG_PWR_DOWN BIT(0)
+
+#define HDMI_PHY_CHG_PWR 0xe1
+#define v_CLK_CHG_PWR(n) (((n) & 1) << 3)
+#define v_DATA_CHG_PWR(n) (((n) & 7) << 0)
+
+#define HDMI_PHY_DRIVER 0xe2
+#define v_CLK_MAIN_DRIVER(n) ((n) << 4)
+#define v_DATA_MAIN_DRIVER(n) ((n) << 0)
+
+#define HDMI_PHY_PRE_EMPHASIS 0xe3
+#define v_PRE_EMPHASIS(n) (((n) & 7) << 4)
+#define v_CLK_PRE_DRIVER(n) (((n) & 3) << 2)
+#define v_DATA_PRE_DRIVER(n) (((n) & 3) << 0)
+
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_LOW 0xe7
+#define v_FEEDBACK_DIV_LOW(n) ((n) & 0xff)
+#define HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH 0xe8
+#define v_FEEDBACK_DIV_HIGH(n) ((n) & 1)
+
+#define HDMI_PHY_PRE_DIV_RATIO 0xed
+#define v_PRE_DIV_RATIO(n) ((n) & 0x1f)
+
+#define HDMI_CEC_CTRL 0xd0
+#define m_ADJUST_FOR_HISENSE BIT(6)
+#define m_REJECT_RX_BROADCAST BIT(5)
+#define m_BUSFREETIME_ENABLE BIT(2)
+#define m_REJECT_RX BIT(1)
+#define m_START_TX BIT(0)
+
+#define HDMI_CEC_DATA 0xd1
+#define HDMI_CEC_TX_OFFSET 0xd2
+#define HDMI_CEC_RX_OFFSET 0xd3
+#define HDMI_CEC_CLK_H 0xd4
+#define HDMI_CEC_CLK_L 0xd5
+#define HDMI_CEC_TX_LENGTH 0xd6
+#define HDMI_CEC_RX_LENGTH 0xd7
+#define HDMI_CEC_TX_INT_MASK 0xd8
+#define m_TX_DONE BIT(3)
+#define m_TX_NOACK BIT(2)
+#define m_TX_BROADCAST_REJ BIT(1)
+#define m_TX_BUSNOTFREE BIT(0)
+
+#define HDMI_CEC_RX_INT_MASK 0xd9
+#define m_RX_LA_ERR BIT(4)
+#define m_RX_GLITCH BIT(3)
+#define m_RX_DONE BIT(0)
+
+#define HDMI_CEC_TX_INT 0xda
+#define HDMI_CEC_RX_INT 0xdb
+#define HDMI_CEC_BUSFREETIME_L 0xdc
+#define HDMI_CEC_BUSFREETIME_H 0xdd
+#define HDMI_CEC_LOGICADDR 0xde
+
+struct inno_hdmi_i2c {
+ struct i2c_adapter adap;
+
+ u8 ddc_addr;
+ u8 segment_addr;
+
+ struct mutex lock;
+ struct completion cmp;
+};
+
+struct inno_hdmi {
+ struct device *dev;
+ struct drm_bridge bridge;
+ struct clk *pclk;
+ struct clk *refclk;
+ void __iomem *regs;
+ struct regmap *grf;
+
+ struct inno_hdmi_i2c *i2c;
+ struct i2c_adapter *ddc;
+ const struct inno_hdmi_plat_data *plat_data;
+};
+
+enum {
+ CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
+ CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
+ CSC_RGB_0_255_TO_RGB_16_235_8BIT,
+};
+
+static const char coeff_csc[][24] = {
+ /*
+ * RGB2YUV:601 SD mode:
+ * Cb = -0.291G - 0.148R + 0.439B + 128
+ * Y = 0.504G + 0.257R + 0.098B + 16
+ * Cr = -0.368G + 0.439R - 0.071B + 128
+ */
+ {
+ 0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80,
+ 0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e,
+ 0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80
+ },
+ /*
+ * RGB2YUV:709 HD mode:
+ * Cb = - 0.338G - 0.101R + 0.439B + 128
+ * Y = 0.614G + 0.183R + 0.062B + 16
+ * Cr = - 0.399G + 0.439R - 0.040B + 128
+ */
+ {
+ 0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80,
+ 0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10,
+ 0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80
+ },
+ /*
+ * RGB[0:255]2RGB[16:235]:
+ * R' = R x (235-16)/255 + 16;
+ * G' = G x (235-16)/255 + 16;
+ * B' = B x (235-16)/255 + 16;
+ */
+ {
+ 0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10,
+ 0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10
+ },
+};
+
+static struct inno_hdmi *bridge_to_inno_hdmi(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct inno_hdmi, bridge);
+}
+
+static int inno_hdmi_find_phy_config(struct inno_hdmi *hdmi,
+ unsigned long pixelclk)
+{
+ const struct inno_hdmi_phy_config *phy_configs = hdmi->plat_data->phy_configs;
+ int i;
+
+ for (i = 0; phy_configs[i].pixelclock != ~0UL; i++) {
+ if (pixelclk <= phy_configs[i].pixelclock)
+ return i;
+ }
+
+ DRM_DEV_DEBUG(hdmi->dev, "No phy configuration for pixelclock %lu\n",
+ pixelclk);
+
+ return -EINVAL;
+}
+
+static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
+{
+ return readl_relaxed(hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, u32 val)
+{
+ writel_relaxed(val, hdmi->regs + (offset) * 0x04);
+}
+
+static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
+ u32 msk, u32 val)
+{
+ u8 temp = hdmi_readb(hdmi, offset) & ~msk;
+
+ temp |= val & msk;
+ hdmi_writeb(hdmi, offset, temp);
+}
+
+static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi, unsigned long long rate)
+{
+ unsigned long long ddc_bus_freq = rate >> 2;
+
+ do_div(ddc_bus_freq, HDMI_SCL_RATE);
+
+ hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
+ hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
+
+ /* Clear the EDID interrupt flag and mute the interrupt */
+ hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+ hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+}
+
+static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable)
+{
+ if (enable)
+ hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_ON);
+ else
+ hdmi_modb(hdmi, HDMI_SYS_CTRL, m_POWER, v_PWR_OFF);
+}
+
+static void inno_hdmi_standby(struct inno_hdmi *hdmi)
+{
+ inno_hdmi_sys_power(hdmi, false);
+
+ hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00);
+ hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00);
+ hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00);
+ hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+};
+
+static void inno_hdmi_power_up(struct inno_hdmi *hdmi,
+ unsigned long mpixelclock)
+{
+ struct inno_hdmi_phy_config *phy_config;
+ int ret = inno_hdmi_find_phy_config(hdmi, mpixelclock);
+
+ if (ret < 0) {
+ phy_config = hdmi->plat_data->default_phy_config;
+ DRM_DEV_ERROR(hdmi->dev,
+ "Using default phy configuration for TMDS rate %lu",
+ mpixelclock);
+ } else {
+ phy_config = &hdmi->plat_data->phy_configs[ret];
+ }
+
+ inno_hdmi_sys_power(hdmi, false);
+
+ hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, phy_config->pre_emphasis);
+ hdmi_writeb(hdmi, HDMI_PHY_DRIVER, phy_config->voltage_level_control);
+ hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+ hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
+ hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
+ hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f);
+ hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00);
+ hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01);
+
+ inno_hdmi_sys_power(hdmi, true);
+};
+
+static void inno_hdmi_init_hw(struct inno_hdmi *hdmi)
+{
+ u32 val;
+ u32 msk;
+
+ hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL);
+ usleep_range(100, 150);
+
+ hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG);
+ usleep_range(100, 150);
+
+ msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL;
+ val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | v_INT_POL_HIGH;
+ hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
+
+ inno_hdmi_standby(hdmi);
+
+ /*
+ * When the controller isn't configured to an accurate
+ * video timing and there is no reference clock available,
+ * then the TMDS clock source would be switched to PCLK_HDMI,
+ * so we need to init the TMDS rate to PCLK rate, and
+ * reconfigure the DDC clock.
+ */
+ if (hdmi->refclk)
+ inno_hdmi_i2c_init(hdmi, clk_get_rate(hdmi->refclk));
+ else
+ inno_hdmi_i2c_init(hdmi, clk_get_rate(hdmi->pclk));
+
+ /* Unmute hotplug interrupt */
+ hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
+}
+
+static int inno_hdmi_bridge_clear_avi_infoframe(struct drm_bridge *bridge)
+{
+ struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
+
+ hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
+
+ return 0;
+}
+
+static int inno_hdmi_bridge_write_avi_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
+ ssize_t i;
+
+ inno_hdmi_bridge_clear_avi_infoframe(bridge);
+
+ for (i = 0; i < len; i++)
+ hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, buffer[i]);
+
+ return 0;
+}
+
+static int inno_hdmi_bridge_clear_hdmi_infoframe(struct drm_bridge *bridge)
+{
+ drm_warn_once(bridge->encoder->dev, "HDMI VSI not implemented\n");
+
+ return 0;
+}
+
+static int inno_hdmi_bridge_write_hdmi_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ drm_warn_once(bridge->encoder->dev, "HDMI VSI not implemented\n");
+
+ return 0;
+}
+
+static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi,
+ struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct drm_connector_state *conn_state = connector->state;
+ int c0_c2_change = 0;
+ int csc_enable = 0;
+ int csc_mode = 0;
+ int auto_csc = 0;
+ int value;
+ int i;
+ int colorimetry;
+ u8 vic = drm_match_cea_mode(mode);
+
+ if (vic == 6 || vic == 7 || vic == 21 || vic == 22 ||
+ vic == 2 || vic == 3 || vic == 17 || vic == 18)
+ colorimetry = HDMI_COLORIMETRY_ITU_601;
+ else
+ colorimetry = HDMI_COLORIMETRY_ITU_709;
+
+
+ /* Input video mode is SDR RGB24bit, data enable signal from external */
+ hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL |
+ v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444));
+
+ /* Input color hardcode to RGB, and output color hardcode to RGB888 */
+ value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) |
+ v_VIDEO_OUTPUT_COLOR(0) |
+ v_VIDEO_INPUT_CSP(0);
+ hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
+
+ if (conn_state->hdmi.output_format == HDMI_COLORSPACE_RGB) {
+ if (conn_state->hdmi.is_limited_range) {
+ csc_mode = CSC_RGB_0_255_TO_RGB_16_235_8BIT;
+ auto_csc = AUTO_CSC_DISABLE;
+ c0_c2_change = C0_C2_CHANGE_DISABLE;
+ csc_enable = v_CSC_ENABLE;
+
+ } else {
+ value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
+ hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+
+ hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
+ m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
+ v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
+ v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
+ return 0;
+ }
+ } else {
+ if (colorimetry == HDMI_COLORIMETRY_ITU_601) {
+ if (conn_state->hdmi.output_format == HDMI_COLORSPACE_YUV444) {
+ csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
+ auto_csc = AUTO_CSC_DISABLE;
+ c0_c2_change = C0_C2_CHANGE_DISABLE;
+ csc_enable = v_CSC_ENABLE;
+ }
+ } else {
+ if (conn_state->hdmi.output_format == HDMI_COLORSPACE_YUV444) {
+ csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
+ auto_csc = AUTO_CSC_DISABLE;
+ c0_c2_change = C0_C2_CHANGE_DISABLE;
+ csc_enable = v_CSC_ENABLE;
+ }
+ }
+ }
+
+ for (i = 0; i < 24; i++)
+ hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i, coeff_csc[csc_mode][i]);
+
+ value = v_SOF_DISABLE | csc_enable | v_COLOR_DEPTH_NOT_INDICATED(1);
+ hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+ hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC |
+ m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) |
+ v_VIDEO_C0_C2_SWAP(c0_c2_change));
+
+ return 0;
+}
+
+static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
+ struct drm_display_mode *mode)
+{
+ const struct inno_hdmi_plat_ops *plat_ops = hdmi->plat_data->ops;
+ u32 value;
+
+ if (plat_ops && plat_ops->enable)
+ plat_ops->enable(hdmi->dev, mode);
+
+ /* Set detail external video timing polarity and interlace mode */
+ value = v_EXTERANL_VIDEO(1);
+ value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
+ v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
+ value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
+ v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
+ value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
+ v_INETLACE(1) : v_INETLACE(0);
+ hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
+
+ /* Set detail external video timing */
+ value = mode->htotal;
+ hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
+ hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
+
+ value = mode->htotal - mode->hdisplay;
+ hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
+ hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
+
+ value = mode->htotal - mode->hsync_start;
+ hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
+ hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
+
+ value = mode->hsync_end - mode->hsync_start;
+ hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
+ hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
+
+ value = mode->vtotal;
+ hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
+ hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
+
+ value = mode->vtotal - mode->vdisplay;
+ hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
+
+ value = mode->vtotal - mode->vsync_start;
+ hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
+
+ value = mode->vsync_end - mode->vsync_start;
+ hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
+
+ hdmi_writeb(hdmi, HDMI_PHY_PRE_DIV_RATIO, 0x1e);
+ hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c);
+ hdmi_writeb(hdmi, HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01);
+
+ return 0;
+}
+
+static int inno_hdmi_setup(struct inno_hdmi *hdmi, struct drm_atomic_state *state)
+{
+ struct drm_bridge *bridge = &hdmi->bridge;
+ struct drm_connector *connector;
+ struct drm_display_info *info;
+ struct drm_connector_state *new_conn_state;
+ struct drm_crtc_state *new_crtc_state;
+
+ connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
+
+ new_conn_state = drm_atomic_get_new_connector_state(state, connector);
+ if (WARN_ON(!new_conn_state))
+ return -EINVAL;
+
+ new_crtc_state = drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
+ if (WARN_ON(!new_crtc_state))
+ return -EINVAL;
+
+ info = &connector->display_info;
+
+ /* Mute video and audio output */
+ hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+ v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
+
+ /* Set HDMI Mode */
+ hdmi_writeb(hdmi, HDMI_HDCP_CTRL, v_HDMI_DVI(info->is_hdmi));
+
+ inno_hdmi_config_video_timing(hdmi, &new_crtc_state->adjusted_mode);
+
+ inno_hdmi_config_video_csc(hdmi, connector, &new_crtc_state->adjusted_mode);
+
+ drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
+
+ /*
+ * When IP controller have configured to an accurate video
+ * timing, then the TMDS clock source would be switched to
+ * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
+ * clock rate, and reconfigure the DDC clock.
+ */
+ inno_hdmi_i2c_init(hdmi, new_conn_state->hdmi.tmds_char_rate);
+
+ /* Unmute video and audio output */
+ hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
+ v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
+
+ inno_hdmi_power_up(hdmi, new_conn_state->hdmi.tmds_char_rate);
+
+ return 0;
+}
+
+static enum drm_mode_status inno_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
+ unsigned long mpixelclk, max_tolerance;
+ long rounded_refclk;
+
+ /* No support for double-clock modes */
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ return MODE_BAD;
+
+ mpixelclk = mode->clock * 1000;
+
+ if (mpixelclk < INNO_HDMI_MIN_TMDS_CLOCK)
+ return MODE_CLOCK_LOW;
+
+ if (inno_hdmi_find_phy_config(hdmi, mpixelclk) < 0)
+ return MODE_CLOCK_HIGH;
+
+ if (hdmi->refclk) {
+ rounded_refclk = clk_round_rate(hdmi->refclk, mpixelclk);
+ if (rounded_refclk < 0)
+ return MODE_BAD;
+
+ /* Vesa DMT standard mentions +/- 0.5% max tolerance */
+ max_tolerance = mpixelclk / 200;
+ if (abs_diff((unsigned long)rounded_refclk, mpixelclk) > max_tolerance)
+ return MODE_NOCLOCK;
+ }
+
+ return MODE_OK;
+}
+
+static enum drm_connector_status
+inno_hdmi_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector)
+{
+ struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
+
+ return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
+ connector_status_connected : connector_status_disconnected;
+}
+
+static const struct drm_edid *
+inno_hdmi_bridge_edid_read(struct drm_bridge *bridge, struct drm_connector *connector)
+{
+ struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
+ const struct drm_edid *drm_edid;
+
+ drm_edid = drm_edid_read_ddc(connector, bridge->ddc);
+ if (!drm_edid)
+ dev_dbg(hdmi->dev, "failed to get edid\n");
+
+ return drm_edid;
+}
+
+static void inno_hdmi_bridge_atomic_enable(struct drm_bridge *bridge,
+ struct drm_atomic_state *state)
+{
+ struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
+
+ inno_hdmi_setup(hdmi, state);
+}
+
+static void inno_hdmi_bridge_atomic_disable(struct drm_bridge *bridge,
+ struct drm_atomic_state *state)
+{
+ struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
+
+ inno_hdmi_standby(hdmi);
+}
+
+static const struct drm_bridge_funcs inno_hdmi_bridge_funcs = {
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_enable = inno_hdmi_bridge_atomic_enable,
+ .atomic_disable = inno_hdmi_bridge_atomic_disable,
+ .detect = inno_hdmi_bridge_detect,
+ .edid_read = inno_hdmi_bridge_edid_read,
+ .hdmi_clear_avi_infoframe = inno_hdmi_bridge_clear_avi_infoframe,
+ .hdmi_write_avi_infoframe = inno_hdmi_bridge_write_avi_infoframe,
+ .hdmi_clear_hdmi_infoframe = inno_hdmi_bridge_clear_hdmi_infoframe,
+ .hdmi_write_hdmi_infoframe = inno_hdmi_bridge_write_hdmi_infoframe,
+ .mode_valid = inno_hdmi_bridge_mode_valid,
+};
+
+static irqreturn_t inno_hdmi_i2c_irq(struct inno_hdmi *hdmi)
+{
+ struct inno_hdmi_i2c *i2c = hdmi->i2c;
+ u8 stat;
+
+ stat = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
+ if (!(stat & m_INT_EDID_READY))
+ return IRQ_NONE;
+
+ /* Clear HDMI EDID interrupt flag */
+ hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+ complete(&i2c->cmp);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t inno_hdmi_hardirq(int irq, void *dev_id)
+{
+ struct inno_hdmi *hdmi = dev_id;
+ irqreturn_t ret = IRQ_NONE;
+ u8 interrupt;
+
+ if (hdmi->i2c)
+ ret = inno_hdmi_i2c_irq(hdmi);
+
+ interrupt = hdmi_readb(hdmi, HDMI_STATUS);
+ if (interrupt & m_INT_HOTPLUG) {
+ hdmi_modb(hdmi, HDMI_STATUS, m_INT_HOTPLUG, m_INT_HOTPLUG);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+}
+
+static irqreturn_t inno_hdmi_irq(int irq, void *dev_id)
+{
+ struct inno_hdmi *hdmi = dev_id;
+
+ drm_helper_hpd_irq_event(hdmi->bridge.dev);
+
+ return IRQ_HANDLED;
+}
+
+static int inno_hdmi_i2c_read(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+ int length = msgs->len;
+ u8 *buf = msgs->buf;
+ int ret;
+
+ ret = wait_for_completion_timeout(&hdmi->i2c->cmp, HZ / 10);
+ if (!ret)
+ return -EAGAIN;
+
+ while (length--)
+ *buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
+
+ return 0;
+}
+
+static int inno_hdmi_i2c_write(struct inno_hdmi *hdmi, struct i2c_msg *msgs)
+{
+ /*
+ * The DDC module only support read EDID message, so
+ * we assume that each word write to this i2c adapter
+ * should be the offset of EDID word address.
+ */
+ if (msgs->len != 1 || (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR))
+ return -EINVAL;
+
+ reinit_completion(&hdmi->i2c->cmp);
+
+ if (msgs->addr == DDC_SEGMENT_ADDR)
+ hdmi->i2c->segment_addr = msgs->buf[0];
+ if (msgs->addr == DDC_ADDR)
+ hdmi->i2c->ddc_addr = msgs->buf[0];
+
+ /* Set edid fifo first addr */
+ hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
+
+ /* Set edid word address 0x00/0x80 */
+ hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
+
+ /* Set edid segment pointer */
+ hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
+
+ return 0;
+}
+
+static int inno_hdmi_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct inno_hdmi *hdmi = i2c_get_adapdata(adap);
+ struct inno_hdmi_i2c *i2c = hdmi->i2c;
+ int i, ret = 0;
+
+ mutex_lock(&i2c->lock);
+
+ /* Clear the EDID interrupt flag and unmute the interrupt */
+ hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, m_INT_EDID_READY);
+ hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY);
+
+ for (i = 0; i < num; i++) {
+ DRM_DEV_DEBUG(hdmi->dev,
+ "xfer: num: %d/%d, len: %d, flags: %#x\n",
+ i + 1, num, msgs[i].len, msgs[i].flags);
+
+ if (msgs[i].flags & I2C_M_RD)
+ ret = inno_hdmi_i2c_read(hdmi, &msgs[i]);
+ else
+ ret = inno_hdmi_i2c_write(hdmi, &msgs[i]);
+
+ if (ret < 0)
+ break;
+ }
+
+ if (!ret)
+ ret = num;
+
+ /* Mute HDMI EDID interrupt */
+ hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+
+ mutex_unlock(&i2c->lock);
+
+ return ret;
+}
+
+static u32 inno_hdmi_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm inno_hdmi_algorithm = {
+ .master_xfer = inno_hdmi_i2c_xfer,
+ .functionality = inno_hdmi_i2c_func,
+};
+
+static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi)
+{
+ struct i2c_adapter *adap;
+ struct inno_hdmi_i2c *i2c;
+ int ret;
+
+ i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_init(&i2c->lock);
+ init_completion(&i2c->cmp);
+
+ adap = &i2c->adap;
+ adap->owner = THIS_MODULE;
+ adap->dev.parent = hdmi->dev;
+ adap->dev.of_node = hdmi->dev->of_node;
+ adap->algo = &inno_hdmi_algorithm;
+ strscpy(adap->name, "Inno HDMI", sizeof(adap->name));
+ i2c_set_adapdata(adap, hdmi);
+
+ ret = devm_i2c_add_adapter(hdmi->dev, adap);
+ if (ret) {
+ dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
+ return ERR_PTR(ret);
+ }
+
+ hdmi->i2c = i2c;
+
+ DRM_DEV_INFO(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
+
+ return adap;
+}
+
+struct inno_hdmi *inno_hdmi_bind(struct device *dev,
+ struct drm_encoder *encoder,
+ const struct inno_hdmi_plat_data *plat_data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct inno_hdmi *hdmi;
+ int irq;
+ int ret;
+
+ if (!plat_data->phy_configs || !plat_data->default_phy_config) {
+ dev_err(dev, "Missing platform PHY ops\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ hdmi = devm_drm_bridge_alloc(dev, struct inno_hdmi, bridge, &inno_hdmi_bridge_funcs);
+ if (IS_ERR(hdmi))
+ return ERR_CAST(hdmi);
+
+ hdmi->dev = dev;
+ hdmi->plat_data = plat_data;
+
+ hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(hdmi->regs))
+ return ERR_CAST(hdmi->regs);
+
+ hdmi->pclk = devm_clk_get_enabled(hdmi->dev, "pclk");
+ if (IS_ERR(hdmi->pclk)) {
+ dev_err_probe(dev, PTR_ERR(hdmi->pclk), "Unable to get HDMI pclk\n");
+ return ERR_CAST(hdmi->pclk);
+ }
+
+ hdmi->refclk = devm_clk_get_optional_enabled(hdmi->dev, "ref");
+ if (IS_ERR(hdmi->refclk)) {
+ dev_err_probe(dev, PTR_ERR(hdmi->refclk), "Unable to get HDMI refclk\n");
+ return ERR_CAST(hdmi->refclk);
+ }
+
+ inno_hdmi_init_hw(hdmi);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return ERR_PTR(irq);
+
+ ret = devm_request_threaded_irq(dev, irq, inno_hdmi_hardirq,
+ inno_hdmi_irq, IRQF_SHARED,
+ dev_name(dev), hdmi);
+ if (ret)
+ return ERR_PTR(ret);
+
+ hdmi->bridge.driver_private = hdmi;
+ hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT |
+ DRM_BRIDGE_OP_EDID |
+ DRM_BRIDGE_OP_HDMI |
+ DRM_BRIDGE_OP_HPD;
+ hdmi->bridge.of_node = pdev->dev.of_node;
+ hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
+ hdmi->bridge.vendor = "Inno";
+ hdmi->bridge.product = "Inno HDMI";
+
+ hdmi->bridge.ddc = inno_hdmi_i2c_adapter(hdmi);
+ if (IS_ERR(hdmi->bridge.ddc))
+ return ERR_CAST(hdmi->bridge.ddc);
+
+ ret = devm_drm_bridge_add(dev, &hdmi->bridge);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return hdmi;
+}
+EXPORT_SYMBOL_GPL(inno_hdmi_bind);
+MODULE_AUTHOR("Andy Yan <andyshrk@163.com>");
+MODULE_DESCRIPTION("INNOSILICON HDMI transmitter library");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/bridge/ite-it6263.c b/drivers/gpu/drm/bridge/ite-it6263.c
index 2eb8fba7016c..3991fb76143c 100644
--- a/drivers/gpu/drm/bridge/ite-it6263.c
+++ b/drivers/gpu/drm/bridge/ite-it6263.c
@@ -759,61 +759,62 @@ it6263_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
return MODE_OK;
}
-static int it6263_hdmi_clear_infoframe(struct drm_bridge *bridge,
- enum hdmi_infoframe_type type)
+static int it6263_hdmi_clear_avi_infoframe(struct drm_bridge *bridge)
{
struct it6263 *it = bridge_to_it6263(bridge);
- switch (type) {
- case HDMI_INFOFRAME_TYPE_AVI:
- regmap_write(it->hdmi_regmap, HDMI_REG_AVI_INFOFRM_CTRL, 0);
- break;
- case HDMI_INFOFRAME_TYPE_VENDOR:
- regmap_write(it->hdmi_regmap, HDMI_REG_PKT_NULL_CTRL, 0);
- break;
- default:
- dev_dbg(it->dev, "unsupported HDMI infoframe 0x%x\n", type);
- }
+ regmap_write(it->hdmi_regmap, HDMI_REG_AVI_INFOFRM_CTRL, 0);
+
+ return 0;
+}
+
+static int it6263_hdmi_clear_hdmi_infoframe(struct drm_bridge *bridge)
+{
+ struct it6263 *it = bridge_to_it6263(bridge);
+
+ regmap_write(it->hdmi_regmap, HDMI_REG_PKT_NULL_CTRL, 0);
return 0;
}
-static int it6263_hdmi_write_infoframe(struct drm_bridge *bridge,
- enum hdmi_infoframe_type type,
- const u8 *buffer, size_t len)
+static int it6263_hdmi_write_avi_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
{
struct it6263 *it = bridge_to_it6263(bridge);
struct regmap *regmap = it->hdmi_regmap;
- switch (type) {
- case HDMI_INFOFRAME_TYPE_AVI:
- /* write the first AVI infoframe data byte chunk(DB1-DB5) */
- regmap_bulk_write(regmap, HDMI_REG_AVI_DB1,
- &buffer[HDMI_INFOFRAME_HEADER_SIZE],
- HDMI_AVI_DB_CHUNK1_SIZE);
-
- /* write the second AVI infoframe data byte chunk(DB6-DB13) */
- regmap_bulk_write(regmap, HDMI_REG_AVI_DB6,
- &buffer[HDMI_INFOFRAME_HEADER_SIZE +
- HDMI_AVI_DB_CHUNK1_SIZE],
- HDMI_AVI_DB_CHUNK2_SIZE);
-
- /* write checksum */
- regmap_write(regmap, HDMI_REG_AVI_CSUM, buffer[3]);
-
- regmap_write(regmap, HDMI_REG_AVI_INFOFRM_CTRL,
- ENABLE_PKT | REPEAT_PKT);
- break;
- case HDMI_INFOFRAME_TYPE_VENDOR:
- /* write header and payload */
- regmap_bulk_write(regmap, HDMI_REG_PKT_HB(0), buffer, len);
-
- regmap_write(regmap, HDMI_REG_PKT_NULL_CTRL,
- ENABLE_PKT | REPEAT_PKT);
- break;
- default:
- dev_dbg(it->dev, "unsupported HDMI infoframe 0x%x\n", type);
- }
+ /* write the first AVI infoframe data byte chunk(DB1-DB5) */
+ regmap_bulk_write(regmap, HDMI_REG_AVI_DB1,
+ &buffer[HDMI_INFOFRAME_HEADER_SIZE],
+ HDMI_AVI_DB_CHUNK1_SIZE);
+
+ /* write the second AVI infoframe data byte chunk(DB6-DB13) */
+ regmap_bulk_write(regmap, HDMI_REG_AVI_DB6,
+ &buffer[HDMI_INFOFRAME_HEADER_SIZE +
+ HDMI_AVI_DB_CHUNK1_SIZE],
+ HDMI_AVI_DB_CHUNK2_SIZE);
+
+ /* write checksum */
+ regmap_write(regmap, HDMI_REG_AVI_CSUM, buffer[3]);
+
+ regmap_write(regmap, HDMI_REG_AVI_INFOFRM_CTRL,
+ ENABLE_PKT | REPEAT_PKT);
+
+ return 0;
+}
+
+static int it6263_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct it6263 *it = bridge_to_it6263(bridge);
+ struct regmap *regmap = it->hdmi_regmap;
+
+ /* write header and payload */
+ regmap_bulk_write(regmap, HDMI_REG_PKT_HB(0), buffer, len);
+
+ regmap_write(regmap, HDMI_REG_PKT_NULL_CTRL,
+ ENABLE_PKT | REPEAT_PKT);
+
return 0;
}
@@ -830,8 +831,10 @@ static const struct drm_bridge_funcs it6263_bridge_funcs = {
.edid_read = it6263_bridge_edid_read,
.atomic_get_input_bus_fmts = it6263_bridge_atomic_get_input_bus_fmts,
.hdmi_tmds_char_rate_valid = it6263_hdmi_tmds_char_rate_valid,
- .hdmi_clear_infoframe = it6263_hdmi_clear_infoframe,
- .hdmi_write_infoframe = it6263_hdmi_write_infoframe,
+ .hdmi_clear_avi_infoframe = it6263_hdmi_clear_avi_infoframe,
+ .hdmi_write_avi_infoframe = it6263_hdmi_write_avi_infoframe,
+ .hdmi_clear_hdmi_infoframe = it6263_hdmi_clear_hdmi_infoframe,
+ .hdmi_write_hdmi_infoframe = it6263_hdmi_write_hdmi_infoframe,
};
static int it6263_probe(struct i2c_client *client)
diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c
index 0185f61e6e59..9246e9c15a6e 100644
--- a/drivers/gpu/drm/bridge/ite-it66121.c
+++ b/drivers/gpu/drm/bridge/ite-it66121.c
@@ -298,7 +298,6 @@ struct it66121_chip_info {
struct it66121_ctx {
struct regmap *regmap;
struct drm_bridge bridge;
- struct drm_bridge *next_bridge;
struct drm_connector *connector;
struct device *dev;
struct gpio_desc *gpio_reset;
@@ -596,7 +595,7 @@ static int it66121_bridge_attach(struct drm_bridge *bridge,
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
return -EINVAL;
- ret = drm_bridge_attach(encoder, ctx->next_bridge, bridge, flags);
+ ret = drm_bridge_attach(encoder, ctx->bridge.next_bridge, bridge, flags);
if (ret)
return ret;
@@ -1543,9 +1542,9 @@ static int it66121_probe(struct i2c_client *client)
return -EINVAL;
}
- ctx->next_bridge = of_drm_find_bridge(ep);
+ ctx->bridge.next_bridge = of_drm_find_and_get_bridge(ep);
of_node_put(ep);
- if (!ctx->next_bridge) {
+ if (!ctx->bridge.next_bridge) {
dev_dbg(ctx->dev, "Next bridge not found, deferring probe\n");
return -EPROBE_DEFER;
}
diff --git a/drivers/gpu/drm/bridge/lontium-lt8912b.c b/drivers/gpu/drm/bridge/lontium-lt8912b.c
index 342374cb8fc6..8a0b48efca58 100644
--- a/drivers/gpu/drm/bridge/lontium-lt8912b.c
+++ b/drivers/gpu/drm/bridge/lontium-lt8912b.c
@@ -35,7 +35,6 @@ struct lt8912 {
struct regmap *regmap[I2C_MAX_IDX];
struct device_node *host_node;
- struct drm_bridge *hdmi_port;
struct mipi_dsi_device *dsi;
@@ -407,8 +406,8 @@ lt8912_connector_detect(struct drm_connector *connector, bool force)
{
struct lt8912 *lt = connector_to_lt8912(connector);
- if (lt->hdmi_port->ops & DRM_BRIDGE_OP_DETECT)
- return drm_bridge_detect(lt->hdmi_port, connector);
+ if (lt->bridge.next_bridge->ops & DRM_BRIDGE_OP_DETECT)
+ return drm_bridge_detect(lt->bridge.next_bridge, connector);
return lt8912_check_cable_status(lt);
}
@@ -429,7 +428,7 @@ static int lt8912_connector_get_modes(struct drm_connector *connector)
u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
int ret, num;
- drm_edid = drm_bridge_edid_read(lt->hdmi_port, connector);
+ drm_edid = drm_bridge_edid_read(lt->bridge.next_bridge, connector);
drm_edid_connector_update(connector, drm_edid);
if (!drm_edid)
return 0;
@@ -519,8 +518,8 @@ static int lt8912_bridge_connector_init(struct drm_bridge *bridge)
struct lt8912 *lt = bridge_to_lt8912(bridge);
struct drm_connector *connector = &lt->connector;
- if (lt->hdmi_port->ops & DRM_BRIDGE_OP_HPD) {
- drm_bridge_hpd_enable(lt->hdmi_port, lt8912_bridge_hpd_cb, lt);
+ if (lt->bridge.next_bridge->ops & DRM_BRIDGE_OP_HPD) {
+ drm_bridge_hpd_enable(lt->bridge.next_bridge, lt8912_bridge_hpd_cb, lt);
connector->polled = DRM_CONNECTOR_POLL_HPD;
} else {
connector->polled = DRM_CONNECTOR_POLL_CONNECT |
@@ -529,7 +528,7 @@ static int lt8912_bridge_connector_init(struct drm_bridge *bridge)
ret = drm_connector_init(bridge->dev, connector,
&lt8912_connector_funcs,
- lt->hdmi_port->type);
+ lt->bridge.next_bridge->type);
if (ret)
goto exit;
@@ -549,7 +548,7 @@ static int lt8912_bridge_attach(struct drm_bridge *bridge,
struct lt8912 *lt = bridge_to_lt8912(bridge);
int ret;
- ret = drm_bridge_attach(encoder, lt->hdmi_port, bridge,
+ ret = drm_bridge_attach(encoder, lt->bridge.next_bridge, bridge,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret < 0) {
dev_err(lt->dev, "Failed to attach next bridge (%d)\n", ret);
@@ -585,8 +584,8 @@ static void lt8912_bridge_detach(struct drm_bridge *bridge)
lt8912_hard_power_off(lt);
- if (lt->connector.dev && lt->hdmi_port->ops & DRM_BRIDGE_OP_HPD)
- drm_bridge_hpd_disable(lt->hdmi_port);
+ if (lt->connector.dev && lt->bridge.next_bridge->ops & DRM_BRIDGE_OP_HPD)
+ drm_bridge_hpd_disable(lt->bridge.next_bridge);
}
static enum drm_mode_status
@@ -611,8 +610,8 @@ lt8912_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector)
{
struct lt8912 *lt = bridge_to_lt8912(bridge);
- if (lt->hdmi_port->ops & DRM_BRIDGE_OP_DETECT)
- return drm_bridge_detect(lt->hdmi_port, connector);
+ if (lt->bridge.next_bridge->ops & DRM_BRIDGE_OP_DETECT)
+ return drm_bridge_detect(lt->bridge.next_bridge, connector);
return lt8912_check_cable_status(lt);
}
@@ -626,8 +625,8 @@ static const struct drm_edid *lt8912_bridge_edid_read(struct drm_bridge *bridge,
* edid must be read through the ddc bus but it must be
* given to the hdmi connector node.
*/
- if (lt->hdmi_port->ops & DRM_BRIDGE_OP_EDID)
- return drm_bridge_edid_read(lt->hdmi_port, connector);
+ if (lt->bridge.next_bridge->ops & DRM_BRIDGE_OP_EDID)
+ return drm_bridge_edid_read(lt->bridge.next_bridge, connector);
dev_warn(lt->dev, "The connected bridge does not supports DRM_BRIDGE_OP_EDID\n");
return NULL;
@@ -723,8 +722,8 @@ static int lt8912_parse_dt(struct lt8912 *lt)
goto err_free_host_node;
}
- lt->hdmi_port = of_drm_find_bridge(port_node);
- if (!lt->hdmi_port) {
+ lt->bridge.next_bridge = of_drm_find_and_get_bridge(port_node);
+ if (!lt->bridge.next_bridge) {
ret = -EPROBE_DEFER;
dev_err_probe(lt->dev, ret, "%s: Failed to get hdmi port\n", __func__);
goto err_free_host_node;
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c
index a2d032ee4744..0628d8e737ab 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9611.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9611.c
@@ -843,84 +843,96 @@ lt9611_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
#define LT9611_INFOFRAME_AUDIO 0x02
#define LT9611_INFOFRAME_AVI 0x08
#define LT9611_INFOFRAME_SPD 0x10
-#define LT9611_INFOFRAME_VENDOR 0x20
+#define LT9611_INFOFRAME_HDMI 0x20
-static int lt9611_hdmi_clear_infoframe(struct drm_bridge *bridge,
- enum hdmi_infoframe_type type)
+static int lt9611_hdmi_clear_audio_infoframe(struct drm_bridge *bridge)
{
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
- unsigned int mask;
- switch (type) {
- case HDMI_INFOFRAME_TYPE_AUDIO:
- mask = LT9611_INFOFRAME_AUDIO;
- break;
+ regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_AUDIO, 0);
- case HDMI_INFOFRAME_TYPE_AVI:
- mask = LT9611_INFOFRAME_AVI;
- break;
+ return 0;
+}
- case HDMI_INFOFRAME_TYPE_SPD:
- mask = LT9611_INFOFRAME_SPD;
- break;
+static int lt9611_hdmi_clear_avi_infoframe(struct drm_bridge *bridge)
+{
+ struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
- case HDMI_INFOFRAME_TYPE_VENDOR:
- mask = LT9611_INFOFRAME_VENDOR;
- break;
+ regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_AVI, 0);
- default:
- drm_dbg_driver(lt9611->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type);
- mask = 0;
- break;
- }
+ return 0;
+}
+
+static int lt9611_hdmi_clear_spd_infoframe(struct drm_bridge *bridge)
+{
+ struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
- if (mask)
- regmap_update_bits(lt9611->regmap, 0x843d, mask, 0);
+ regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_SPD, 0);
return 0;
}
-static int lt9611_hdmi_write_infoframe(struct drm_bridge *bridge,
- enum hdmi_infoframe_type type,
- const u8 *buffer, size_t len)
+static int lt9611_hdmi_clear_hdmi_infoframe(struct drm_bridge *bridge)
+{
+ struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
+
+ regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_HDMI, 0);
+
+ return 0;
+}
+
+static int lt9611_hdmi_write_audio_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
{
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
- unsigned int mask, addr;
int i;
- switch (type) {
- case HDMI_INFOFRAME_TYPE_AUDIO:
- mask = LT9611_INFOFRAME_AUDIO;
- addr = 0x84b2;
- break;
-
- case HDMI_INFOFRAME_TYPE_AVI:
- mask = LT9611_INFOFRAME_AVI;
- addr = 0x8440;
- break;
-
- case HDMI_INFOFRAME_TYPE_SPD:
- mask = LT9611_INFOFRAME_SPD;
- addr = 0x8493;
- break;
-
- case HDMI_INFOFRAME_TYPE_VENDOR:
- mask = LT9611_INFOFRAME_VENDOR;
- addr = 0x8474;
- break;
-
- default:
- drm_dbg_driver(lt9611->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type);
- mask = 0;
- break;
- }
+ for (i = 0; i < len; i++)
+ regmap_write(lt9611->regmap, 0x84b2 + i, buffer[i]);
- if (mask) {
- for (i = 0; i < len; i++)
- regmap_write(lt9611->regmap, addr + i, buffer[i]);
+ regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_AUDIO, LT9611_INFOFRAME_AUDIO);
- regmap_update_bits(lt9611->regmap, 0x843d, mask, mask);
- }
+ return 0;
+}
+
+static int lt9611_hdmi_write_avi_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
+ int i;
+
+ for (i = 0; i < len; i++)
+ regmap_write(lt9611->regmap, 0x8440 + i, buffer[i]);
+
+ regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_AVI, LT9611_INFOFRAME_AVI);
+
+ return 0;
+}
+
+static int lt9611_hdmi_write_spd_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
+ int i;
+
+ for (i = 0; i < len; i++)
+ regmap_write(lt9611->regmap, 0x8493 + i, buffer[i]);
+
+ regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_SPD, LT9611_INFOFRAME_SPD);
+
+ return 0;
+}
+
+static int lt9611_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
+ int i;
+
+ for (i = 0; i < len; i++)
+ regmap_write(lt9611->regmap, 0x8474 + i, buffer[i]);
+
+ regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_HDMI, LT9611_INFOFRAME_HDMI);
return 0;
}
@@ -1003,8 +1015,14 @@ static const struct drm_bridge_funcs lt9611_bridge_funcs = {
.atomic_get_input_bus_fmts = lt9611_atomic_get_input_bus_fmts,
.hdmi_tmds_char_rate_valid = lt9611_hdmi_tmds_char_rate_valid,
- .hdmi_write_infoframe = lt9611_hdmi_write_infoframe,
- .hdmi_clear_infoframe = lt9611_hdmi_clear_infoframe,
+ .hdmi_write_audio_infoframe = lt9611_hdmi_write_audio_infoframe,
+ .hdmi_clear_audio_infoframe = lt9611_hdmi_clear_audio_infoframe,
+ .hdmi_write_avi_infoframe = lt9611_hdmi_write_avi_infoframe,
+ .hdmi_clear_avi_infoframe = lt9611_hdmi_clear_avi_infoframe,
+ .hdmi_write_spd_infoframe = lt9611_hdmi_write_spd_infoframe,
+ .hdmi_clear_spd_infoframe = lt9611_hdmi_clear_spd_infoframe,
+ .hdmi_write_hdmi_infoframe = lt9611_hdmi_write_hdmi_infoframe,
+ .hdmi_clear_hdmi_infoframe = lt9611_hdmi_clear_hdmi_infoframe,
.hdmi_audio_startup = lt9611_hdmi_audio_startup,
.hdmi_audio_prepare = lt9611_hdmi_audio_prepare,
@@ -1132,7 +1150,8 @@ static int lt9611_probe(struct i2c_client *client)
lt9611->bridge.of_node = client->dev.of_node;
lt9611->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
DRM_BRIDGE_OP_HPD | DRM_BRIDGE_OP_MODES |
- DRM_BRIDGE_OP_HDMI | DRM_BRIDGE_OP_HDMI_AUDIO;
+ DRM_BRIDGE_OP_HDMI | DRM_BRIDGE_OP_HDMI_AUDIO |
+ DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME;
lt9611->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
lt9611->bridge.vendor = "Lontium";
lt9611->bridge.product = "LT9611";
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
index 38fb8776c0f4..11aab07d88df 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
@@ -17,8 +17,6 @@
#include <linux/wait.h>
#include <linux/workqueue.h>
-#include <sound/hdmi-codec.h>
-
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_edid.h>
@@ -27,6 +25,8 @@
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
+#include <drm/display/drm_hdmi_audio_helper.h>
+
#define EDID_BLOCK_SIZE 128
#define EDID_NUM_BLOCKS 2
@@ -48,7 +48,6 @@ struct lt9611uxc {
struct device_node *dsi1_node;
struct mipi_dsi_device *dsi0;
struct mipi_dsi_device *dsi1;
- struct platform_device *audio_pdev;
struct gpio_desc *reset_gpio;
struct gpio_desc *enable_gpio;
@@ -429,12 +428,52 @@ static const struct drm_edid *lt9611uxc_bridge_edid_read(struct drm_bridge *brid
return drm_edid_read_custom(connector, lt9611uxc_get_edid_block, lt9611uxc);
}
+static void lt9611uxc_bridge_hpd_notify(struct drm_bridge *bridge,
+ struct drm_connector *connector,
+ enum drm_connector_status status)
+{
+ const struct drm_edid *drm_edid;
+
+ if (status == connector_status_disconnected) {
+ drm_connector_hdmi_audio_plugged_notify(connector, false);
+ drm_edid_connector_update(connector, NULL);
+ return;
+ }
+
+ drm_edid = lt9611uxc_bridge_edid_read(bridge, connector);
+ drm_edid_connector_update(connector, drm_edid);
+ drm_edid_free(drm_edid);
+
+ if (status == connector_status_connected)
+ drm_connector_hdmi_audio_plugged_notify(connector, true);
+}
+
+static int lt9611uxc_hdmi_audio_prepare(struct drm_bridge *bridge,
+ struct drm_connector *connector,
+ struct hdmi_codec_daifmt *fmt,
+ struct hdmi_codec_params *hparms)
+{
+ /*
+ * LT9611UXC will automatically detect rate and sample size, so no need
+ * to setup anything here.
+ */
+ return 0;
+}
+
+static void lt9611uxc_hdmi_audio_shutdown(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+}
+
static const struct drm_bridge_funcs lt9611uxc_bridge_funcs = {
.attach = lt9611uxc_bridge_attach,
.mode_valid = lt9611uxc_bridge_mode_valid,
.mode_set = lt9611uxc_bridge_mode_set,
.detect = lt9611uxc_bridge_detect,
.edid_read = lt9611uxc_bridge_edid_read,
+ .hpd_notify = lt9611uxc_bridge_hpd_notify,
+ .hdmi_audio_prepare = lt9611uxc_hdmi_audio_prepare,
+ .hdmi_audio_shutdown = lt9611uxc_hdmi_audio_shutdown,
};
static int lt9611uxc_parse_dt(struct device *dev,
@@ -508,73 +547,6 @@ static int lt9611uxc_read_version(struct lt9611uxc *lt9611uxc)
return ret < 0 ? ret : rev;
}
-static int lt9611uxc_hdmi_hw_params(struct device *dev, void *data,
- struct hdmi_codec_daifmt *fmt,
- struct hdmi_codec_params *hparms)
-{
- /*
- * LT9611UXC will automatically detect rate and sample size, so no need
- * to setup anything here.
- */
- return 0;
-}
-
-static void lt9611uxc_audio_shutdown(struct device *dev, void *data)
-{
-}
-
-static int lt9611uxc_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
- struct device_node *endpoint,
- void *data)
-{
- struct of_endpoint of_ep;
- int ret;
-
- ret = of_graph_parse_endpoint(endpoint, &of_ep);
- if (ret < 0)
- return ret;
-
- /*
- * HDMI sound should be located as reg = <2>
- * Then, it is sound port 0
- */
- if (of_ep.port == 2)
- return 0;
-
- return -EINVAL;
-}
-
-static const struct hdmi_codec_ops lt9611uxc_codec_ops = {
- .hw_params = lt9611uxc_hdmi_hw_params,
- .audio_shutdown = lt9611uxc_audio_shutdown,
- .get_dai_id = lt9611uxc_hdmi_i2s_get_dai_id,
-};
-
-static int lt9611uxc_audio_init(struct device *dev, struct lt9611uxc *lt9611uxc)
-{
- struct hdmi_codec_pdata codec_data = {
- .ops = &lt9611uxc_codec_ops,
- .max_i2s_channels = 2,
- .i2s = 1,
- .data = lt9611uxc,
- };
-
- lt9611uxc->audio_pdev =
- platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
- PLATFORM_DEVID_AUTO,
- &codec_data, sizeof(codec_data));
-
- return PTR_ERR_OR_ZERO(lt9611uxc->audio_pdev);
-}
-
-static void lt9611uxc_audio_exit(struct lt9611uxc *lt9611uxc)
-{
- if (lt9611uxc->audio_pdev) {
- platform_device_unregister(lt9611uxc->audio_pdev);
- lt9611uxc->audio_pdev = NULL;
- }
-}
-
#define LT9611UXC_FW_PAGE_SIZE 32
static void lt9611uxc_firmware_write_page(struct lt9611uxc *lt9611uxc, u16 addr, const u8 *buf)
{
@@ -858,11 +830,17 @@ retry:
i2c_set_clientdata(client, lt9611uxc);
lt9611uxc->bridge.of_node = client->dev.of_node;
- lt9611uxc->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID;
+ lt9611uxc->bridge.ops = DRM_BRIDGE_OP_DETECT |
+ DRM_BRIDGE_OP_EDID |
+ DRM_BRIDGE_OP_HDMI_AUDIO;
if (lt9611uxc->hpd_supported)
lt9611uxc->bridge.ops |= DRM_BRIDGE_OP_HPD;
lt9611uxc->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
+ lt9611uxc->bridge.hdmi_audio_dev = dev;
+ lt9611uxc->bridge.hdmi_audio_max_i2s_playback_channels = 2;
+ lt9611uxc->bridge.hdmi_audio_dai_port = 2;
+
drm_bridge_add(&lt9611uxc->bridge);
/* Attach primary DSI */
@@ -881,10 +859,6 @@ retry:
}
}
- ret = lt9611uxc_audio_init(dev, lt9611uxc);
- if (ret)
- goto err_remove_bridge;
-
return 0;
err_remove_bridge:
@@ -908,7 +882,6 @@ static void lt9611uxc_remove(struct i2c_client *client)
free_irq(client->irq, lt9611uxc);
cancel_work_sync(&lt9611uxc->work);
- lt9611uxc_audio_exit(lt9611uxc);
drm_bridge_remove(&lt9611uxc->bridge);
mutex_destroy(&lt9611uxc->ocm_lock);
diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
index eabc4c32f6ab..1d85e706c74b 100644
--- a/drivers/gpu/drm/bridge/samsung-dsim.c
+++ b/drivers/gpu/drm/bridge/samsung-dsim.c
@@ -1828,7 +1828,7 @@ static int samsung_dsim_attach(struct drm_bridge *bridge,
{
struct samsung_dsim *dsi = bridge_to_dsi(bridge);
- return drm_bridge_attach(encoder, dsi->out_bridge, bridge,
+ return drm_bridge_attach(encoder, dsi->bridge.next_bridge, bridge,
flags);
}
@@ -1886,11 +1886,12 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
{
struct samsung_dsim *dsi = host_to_dsi(host);
const struct samsung_dsim_plat_data *pdata = dsi->plat_data;
+ struct drm_bridge *next_bridge __free(drm_bridge_put) = NULL;
struct device *dev = dsi->dev;
struct device_node *np = dev->of_node;
struct device_node *remote;
struct drm_panel *panel;
- int ret;
+ int ret = 0;
/*
* Devices can also be child nodes when we also control that device
@@ -1924,17 +1925,22 @@ of_find_panel_or_bridge:
panel = of_drm_find_panel(remote);
if (!IS_ERR(panel)) {
- dsi->out_bridge = devm_drm_panel_bridge_add(dev, panel);
+ next_bridge = devm_drm_panel_bridge_add(dev, panel);
+ if (IS_ERR(next_bridge)) {
+ ret = PTR_ERR(next_bridge);
+ next_bridge = NULL; // Inhibit the cleanup action on an ERR_PTR
+ } else {
+ drm_bridge_get(next_bridge);
+ }
} else {
- dsi->out_bridge = of_drm_find_bridge(remote);
- if (!dsi->out_bridge)
- dsi->out_bridge = ERR_PTR(-EINVAL);
+ next_bridge = of_drm_find_and_get_bridge(remote);
+ if (!next_bridge)
+ ret = -EINVAL;
}
of_node_put(remote);
- if (IS_ERR(dsi->out_bridge)) {
- ret = PTR_ERR(dsi->out_bridge);
+ if (ret) {
DRM_DEV_ERROR(dev, "failed to find the bridge: %d\n", ret);
return ret;
}
@@ -1958,10 +1964,13 @@ of_find_panel_or_bridge:
return ret;
}
+ // The next bridge can be used by host_ops->attach
+ dsi->bridge.next_bridge = drm_bridge_get(next_bridge);
+
if (pdata->host_ops && pdata->host_ops->attach) {
ret = pdata->host_ops->attach(dsi, device);
if (ret)
- return ret;
+ goto err_release_next_bridge;
}
dsi->lanes = device->lanes;
@@ -1969,6 +1978,11 @@ of_find_panel_or_bridge:
dsi->mode_flags = device->mode_flags;
return 0;
+
+err_release_next_bridge:
+ drm_bridge_put(dsi->bridge.next_bridge);
+ dsi->bridge.next_bridge = NULL;
+ return ret;
}
static void samsung_dsim_unregister_te_irq(struct samsung_dsim *dsi)
@@ -1985,11 +1999,12 @@ static int samsung_dsim_host_detach(struct mipi_dsi_host *host,
struct samsung_dsim *dsi = host_to_dsi(host);
const struct samsung_dsim_plat_data *pdata = dsi->plat_data;
- dsi->out_bridge = NULL;
-
if (pdata->host_ops && pdata->host_ops->detach)
pdata->host_ops->detach(dsi, device);
+ drm_bridge_put(dsi->bridge.next_bridge);
+ dsi->bridge.next_bridge = NULL;
+
samsung_dsim_unregister_te_irq(dsi);
drm_bridge_remove(&dsi->bridge);
diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c
index 1f0aba28ad1e..12497f5ce4ff 100644
--- a/drivers/gpu/drm/bridge/sii902x.c
+++ b/drivers/gpu/drm/bridge/sii902x.c
@@ -175,7 +175,6 @@ struct sii902x {
struct i2c_client *i2c;
struct regmap *regmap;
struct drm_bridge bridge;
- struct drm_bridge *next_bridge;
struct drm_connector connector;
struct gpio_desc *reset_gpio;
struct i2c_mux_core *i2cmux;
@@ -421,7 +420,7 @@ static int sii902x_bridge_attach(struct drm_bridge *bridge,
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
- return drm_bridge_attach(encoder, sii902x->next_bridge,
+ return drm_bridge_attach(encoder, sii902x->bridge.next_bridge,
bridge, flags);
drm_connector_helper_add(&sii902x->connector,
@@ -1204,9 +1203,9 @@ static int sii902x_probe(struct i2c_client *client)
return -ENODEV;
}
- sii902x->next_bridge = of_drm_find_bridge(remote);
+ sii902x->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
of_node_put(remote);
- if (!sii902x->next_bridge)
+ if (!sii902x->bridge.next_bridge)
return dev_err_probe(dev, -EPROBE_DEFER,
"Failed to find remote bridge\n");
}
diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c
index 2cd1847ba776..8aa31ca3c72d 100644
--- a/drivers/gpu/drm/bridge/simple-bridge.c
+++ b/drivers/gpu/drm/bridge/simple-bridge.c
@@ -31,7 +31,6 @@ struct simple_bridge {
const struct simple_bridge_info *info;
- struct drm_bridge *next_bridge;
struct regulator *vdd;
struct gpio_desc *enable;
};
@@ -54,8 +53,8 @@ static int simple_bridge_get_modes(struct drm_connector *connector)
const struct drm_edid *drm_edid;
int ret;
- if (sbridge->next_bridge->ops & DRM_BRIDGE_OP_EDID) {
- drm_edid = drm_bridge_edid_read(sbridge->next_bridge, connector);
+ if (sbridge->bridge.next_bridge->ops & DRM_BRIDGE_OP_EDID) {
+ drm_edid = drm_bridge_edid_read(sbridge->bridge.next_bridge, connector);
if (!drm_edid)
DRM_INFO("EDID read failed. Fallback to standard modes\n");
} else {
@@ -90,7 +89,7 @@ simple_bridge_connector_detect(struct drm_connector *connector, bool force)
{
struct simple_bridge *sbridge = drm_connector_to_simple_bridge(connector);
- return drm_bridge_detect(sbridge->next_bridge, connector);
+ return drm_bridge_detect(sbridge->bridge.next_bridge, connector);
}
static const struct drm_connector_funcs simple_bridge_con_funcs = {
@@ -109,7 +108,7 @@ static int simple_bridge_attach(struct drm_bridge *bridge,
struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
int ret;
- ret = drm_bridge_attach(encoder, sbridge->next_bridge, bridge,
+ ret = drm_bridge_attach(encoder, sbridge->bridge.next_bridge, bridge,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret < 0)
return ret;
@@ -122,7 +121,7 @@ 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->next_bridge->ddc);
+ sbridge->bridge.next_bridge->ddc);
if (ret) {
DRM_ERROR("Failed to initialize connector\n");
return ret;
@@ -180,10 +179,10 @@ static int simple_bridge_probe(struct platform_device *pdev)
if (!remote)
return -EINVAL;
- sbridge->next_bridge = of_drm_find_bridge(remote);
+ sbridge->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
of_node_put(remote);
- if (!sbridge->next_bridge) {
+ if (!sbridge->bridge.next_bridge) {
dev_dbg(&pdev->dev, "Next bridge not found, deferring probe\n");
return -EPROBE_DEFER;
}
@@ -262,6 +261,11 @@ static const struct of_device_id simple_bridge_match[] = {
.connector_type = DRM_MODE_CONNECTOR_VGA,
},
}, {
+ .compatible = "algoltek,ag6311",
+ .data = &(const struct simple_bridge_info) {
+ .connector_type = DRM_MODE_CONNECTOR_HDMIA,
+ },
+ }, {
.compatible = "asl-tek,cs5263",
.data = &(const struct simple_bridge_info) {
.connector_type = DRM_MODE_CONNECTOR_HDMIA,
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
index 60166919c5b5..ab7fed6214e0 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
@@ -26,6 +26,7 @@
#include <drm/drm_connector.h>
#include <drm/drm_edid.h>
#include <drm/drm_modes.h>
+#include <drm/drm_print.h>
#include <media/cec.h>
@@ -166,6 +167,7 @@ struct dw_hdmi_qp {
int main_irq;
unsigned long tmds_char_rate;
+ bool no_hpd;
};
static void dw_hdmi_qp_write(struct dw_hdmi_qp *hdmi, unsigned int val,
@@ -556,14 +558,22 @@ static int dw_hdmi_qp_i2c_read(struct dw_hdmi_qp *hdmi,
stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10);
if (!stat) {
- dev_err(hdmi->dev, "i2c read timed out\n");
+ if (hdmi->no_hpd)
+ dev_dbg_ratelimited(hdmi->dev,
+ "i2c read timed out\n");
+ else
+ dev_err(hdmi->dev, "i2c read timed out\n");
dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
return -EAGAIN;
}
/* Check for error condition on the bus */
if (i2c->stat & I2CM_NACK_RCVD_IRQ) {
- dev_err(hdmi->dev, "i2c read error\n");
+ if (hdmi->no_hpd)
+ dev_dbg_ratelimited(hdmi->dev,
+ "i2c read error\n");
+ else
+ dev_err(hdmi->dev, "i2c read error\n");
dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
return -EIO;
}
@@ -901,6 +911,15 @@ static enum drm_connector_status
dw_hdmi_qp_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
+ const struct drm_edid *drm_edid;
+
+ if (hdmi->no_hpd) {
+ drm_edid = drm_edid_read_ddc(connector, bridge->ddc);
+ if (drm_edid)
+ return connector_status_connected;
+ else
+ return connector_status_disconnected;
+ }
return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
}
@@ -926,6 +945,11 @@ dw_hdmi_qp_bridge_tmds_char_rate_valid(const struct drm_bridge *bridge,
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
+ /*
+ * TODO: when hdmi->no_hpd is 1 we must not support modes that
+ * require scrambling, including every mode with a clock above
+ * HDMI14_MAX_TMDSCLK.
+ */
if (rate > HDMI14_MAX_TMDSCLK) {
dev_dbg(hdmi->dev, "Unsupported TMDS char rate: %lld\n", rate);
return MODE_CLOCK_HIGH;
@@ -934,57 +958,85 @@ dw_hdmi_qp_bridge_tmds_char_rate_valid(const struct drm_bridge *bridge,
return MODE_OK;
}
-static int dw_hdmi_qp_bridge_clear_infoframe(struct drm_bridge *bridge,
- enum hdmi_infoframe_type type)
+static int dw_hdmi_qp_bridge_clear_avi_infoframe(struct drm_bridge *bridge)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
- switch (type) {
- case HDMI_INFOFRAME_TYPE_AVI:
- dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN,
- PKTSCHED_PKT_EN);
- break;
+ dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN,
+ PKTSCHED_PKT_EN);
- case HDMI_INFOFRAME_TYPE_DRM:
- dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN);
- break;
+ return 0;
+}
- case HDMI_INFOFRAME_TYPE_AUDIO:
- dw_hdmi_qp_mod(hdmi, 0,
- PKTSCHED_ACR_TX_EN |
- PKTSCHED_AUDS_TX_EN |
- PKTSCHED_AUDI_TX_EN,
- PKTSCHED_PKT_EN);
- break;
- default:
- dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type);
- }
+static int dw_hdmi_qp_bridge_clear_hdmi_infoframe(struct drm_bridge *bridge)
+{
+ /* FIXME: add support for this InfoFrame */
+
+ drm_warn_once(bridge->encoder->dev, "HDMI VSI not supported\n");
return 0;
}
-static int dw_hdmi_qp_bridge_write_infoframe(struct drm_bridge *bridge,
- enum hdmi_infoframe_type type,
- const u8 *buffer, size_t len)
+static int dw_hdmi_qp_bridge_clear_hdr_drm_infoframe(struct drm_bridge *bridge)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
- dw_hdmi_qp_bridge_clear_infoframe(bridge, type);
+ dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN);
- switch (type) {
- case HDMI_INFOFRAME_TYPE_AVI:
- return dw_hdmi_qp_config_avi_infoframe(hdmi, buffer, len);
+ return 0;
+}
- case HDMI_INFOFRAME_TYPE_DRM:
- return dw_hdmi_qp_config_drm_infoframe(hdmi, buffer, len);
+static int dw_hdmi_qp_bridge_clear_audio_infoframe(struct drm_bridge *bridge)
+{
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
- case HDMI_INFOFRAME_TYPE_AUDIO:
- return dw_hdmi_qp_config_audio_infoframe(hdmi, buffer, len);
+ dw_hdmi_qp_mod(hdmi, 0,
+ PKTSCHED_ACR_TX_EN |
+ PKTSCHED_AUDS_TX_EN |
+ PKTSCHED_AUDI_TX_EN,
+ PKTSCHED_PKT_EN);
- default:
- dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type);
- return 0;
- }
+ return 0;
+}
+
+static int dw_hdmi_qp_bridge_write_avi_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
+
+ dw_hdmi_qp_bridge_clear_avi_infoframe(bridge);
+
+ return dw_hdmi_qp_config_avi_infoframe(hdmi, buffer, len);
+}
+
+static int dw_hdmi_qp_bridge_write_hdmi_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ dw_hdmi_qp_bridge_clear_hdmi_infoframe(bridge);
+
+ /* FIXME: add support for the HDMI VSI */
+
+ return 0;
+}
+
+static int dw_hdmi_qp_bridge_write_hdr_drm_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
+
+ dw_hdmi_qp_bridge_clear_hdr_drm_infoframe(bridge);
+
+ return dw_hdmi_qp_config_drm_infoframe(hdmi, buffer, len);
+}
+
+static int dw_hdmi_qp_bridge_write_audio_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
+{
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
+
+ dw_hdmi_qp_bridge_clear_audio_infoframe(bridge);
+
+ return dw_hdmi_qp_config_audio_infoframe(hdmi, buffer, len);
}
#ifdef CONFIG_DRM_DW_HDMI_QP_CEC
@@ -1169,8 +1221,14 @@ static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
.detect = dw_hdmi_qp_bridge_detect,
.edid_read = dw_hdmi_qp_bridge_edid_read,
.hdmi_tmds_char_rate_valid = dw_hdmi_qp_bridge_tmds_char_rate_valid,
- .hdmi_clear_infoframe = dw_hdmi_qp_bridge_clear_infoframe,
- .hdmi_write_infoframe = dw_hdmi_qp_bridge_write_infoframe,
+ .hdmi_clear_avi_infoframe = dw_hdmi_qp_bridge_clear_avi_infoframe,
+ .hdmi_write_avi_infoframe = dw_hdmi_qp_bridge_write_avi_infoframe,
+ .hdmi_clear_hdmi_infoframe = dw_hdmi_qp_bridge_clear_hdmi_infoframe,
+ .hdmi_write_hdmi_infoframe = dw_hdmi_qp_bridge_write_hdmi_infoframe,
+ .hdmi_clear_hdr_drm_infoframe = dw_hdmi_qp_bridge_clear_hdr_drm_infoframe,
+ .hdmi_write_hdr_drm_infoframe = dw_hdmi_qp_bridge_write_hdr_drm_infoframe,
+ .hdmi_clear_audio_infoframe = dw_hdmi_qp_bridge_clear_audio_infoframe,
+ .hdmi_write_audio_infoframe = dw_hdmi_qp_bridge_write_audio_infoframe,
.hdmi_audio_startup = dw_hdmi_qp_audio_enable,
.hdmi_audio_shutdown = dw_hdmi_qp_audio_disable,
.hdmi_audio_prepare = dw_hdmi_qp_audio_prepare,
@@ -1279,12 +1337,16 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
if (ret)
return ERR_PTR(ret);
+ hdmi->no_hpd = device_property_read_bool(dev, "no-hpd");
+
hdmi->bridge.driver_private = hdmi;
hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT |
DRM_BRIDGE_OP_EDID |
DRM_BRIDGE_OP_HDMI |
DRM_BRIDGE_OP_HDMI_AUDIO |
- DRM_BRIDGE_OP_HPD;
+ DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME;
+ if (!hdmi->no_hpd)
+ hdmi->bridge.ops |= DRM_BRIDGE_OP_HPD;
hdmi->bridge.of_node = pdev->dev.of_node;
hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
hdmi->bridge.vendor = "Synopsys";
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 3b77e73ac0ea..ee88c0e793b0 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -132,7 +132,6 @@ struct dw_hdmi_phy_data {
struct dw_hdmi {
struct drm_connector connector;
struct drm_bridge bridge;
- struct drm_bridge *next_bridge;
unsigned int version;
@@ -2912,7 +2911,7 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge,
struct dw_hdmi *hdmi = bridge->driver_private;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
- return drm_bridge_attach(encoder, hdmi->next_bridge,
+ return drm_bridge_attach(encoder, hdmi->bridge.next_bridge,
bridge, flags);
return dw_hdmi_connector_create(hdmi);
@@ -3318,9 +3317,9 @@ static int dw_hdmi_parse_dt(struct dw_hdmi *hdmi)
if (!remote)
return -ENODEV;
- hdmi->next_bridge = of_drm_find_bridge(remote);
+ hdmi->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
of_node_put(remote);
- if (!hdmi->next_bridge)
+ if (!hdmi->bridge.next_bridge)
return -EPROBE_DEFER;
return 0;
diff --git a/drivers/gpu/drm/bridge/thc63lvd1024.c b/drivers/gpu/drm/bridge/thc63lvd1024.c
index 2cb7cd0c0608..c804222846c3 100644
--- a/drivers/gpu/drm/bridge/thc63lvd1024.c
+++ b/drivers/gpu/drm/bridge/thc63lvd1024.c
@@ -32,7 +32,6 @@ struct thc63_dev {
struct gpio_desc *oe;
struct drm_bridge bridge;
- struct drm_bridge *next;
struct drm_bridge_timings timings;
};
@@ -48,7 +47,7 @@ static int thc63_attach(struct drm_bridge *bridge,
{
struct thc63_dev *thc63 = to_thc63(bridge);
- return drm_bridge_attach(encoder, thc63->next, bridge, flags);
+ return drm_bridge_attach(encoder, thc63->bridge.next_bridge, bridge, flags);
}
static enum drm_mode_status thc63_mode_valid(struct drm_bridge *bridge,
@@ -132,9 +131,9 @@ static int thc63_parse_dt(struct thc63_dev *thc63)
return -ENODEV;
}
- thc63->next = of_drm_find_bridge(remote);
+ thc63->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
of_node_put(remote);
- if (!thc63->next)
+ if (!thc63->bridge.next_bridge)
return -EPROBE_DEFER;
endpoint = of_graph_get_endpoint_by_regs(thc63->dev->of_node,
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
index fffb47b62f43..f6736b4457bb 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
@@ -406,6 +406,10 @@ static void sn65dsi83_reset_work(struct work_struct *ws)
{
struct sn65dsi83 *ctx = container_of(ws, struct sn65dsi83, reset_work);
int ret;
+ int idx;
+
+ if (!drm_bridge_enter(&ctx->bridge, &idx))
+ return;
/* Reset the pipe */
ret = sn65dsi83_reset_pipe(ctx);
@@ -415,12 +419,18 @@ static void sn65dsi83_reset_work(struct work_struct *ws)
}
if (ctx->irq)
enable_irq(ctx->irq);
+
+ drm_bridge_exit(idx);
}
static void sn65dsi83_handle_errors(struct sn65dsi83 *ctx)
{
unsigned int irq_stat;
int ret;
+ int idx;
+
+ if (!drm_bridge_enter(&ctx->bridge, &idx))
+ return;
/*
* Schedule a reset in case of:
@@ -448,6 +458,8 @@ static void sn65dsi83_handle_errors(struct sn65dsi83 *ctx)
schedule_work(&ctx->reset_work);
}
+
+ drm_bridge_exit(idx);
}
static void sn65dsi83_monitor_work(struct work_struct *work)
@@ -470,6 +482,37 @@ static void sn65dsi83_monitor_stop(struct sn65dsi83 *ctx)
cancel_delayed_work_sync(&ctx->monitor_work);
}
+/*
+ * Release resources taken by sn65dsi83_atomic_pre_enable().
+ *
+ * Invoked by sn65dsi83_atomic_disable() normally, or by devres after
+ * sn65dsi83_remove() in case this happens befora atomic_disable.
+ */
+static void sn65dsi83_release_resources(void *data)
+{
+ struct sn65dsi83 *ctx = (struct sn65dsi83 *)data;
+ int ret;
+
+ if (ctx->irq) {
+ /* Disable irq */
+ regmap_write(ctx->regmap, REG_IRQ_EN, 0x0);
+ regmap_write(ctx->regmap, REG_IRQ_GLOBAL, 0x0);
+ } else {
+ /* Stop the polling task */
+ sn65dsi83_monitor_stop(ctx);
+ }
+
+ /* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */
+ gpiod_set_value_cansleep(ctx->enable_gpio, 0);
+ usleep_range(10000, 11000);
+
+ ret = regulator_disable(ctx->vcc);
+ if (ret)
+ dev_err(ctx->dev, "Failed to disable vcc: %d\n", ret);
+
+ regcache_mark_dirty(ctx->regmap);
+}
+
static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
@@ -485,11 +528,15 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge,
__le16 le16val;
u16 val;
int ret;
+ int idx;
+
+ if (!drm_bridge_enter(bridge, &idx))
+ return;
ret = regulator_enable(ctx->vcc);
if (ret) {
dev_err(ctx->dev, "Failed to enable vcc: %d\n", ret);
- return;
+ goto err_exit;
}
/* Deassert reset */
@@ -632,7 +679,7 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge,
dev_err(ctx->dev, "failed to lock PLL, ret=%i\n", ret);
/* On failure, disable PLL again and exit. */
regmap_write(ctx->regmap, REG_RC_PLL_EN, 0x00);
- return;
+ goto err_add_action;
}
/* Trigger reset after CSR register update. */
@@ -640,6 +687,11 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge,
/* Wait for 10ms after soft reset as specified in datasheet */
usleep_range(10000, 12000);
+
+err_add_action:
+ devm_add_action(ctx->dev, sn65dsi83_release_resources, ctx);
+err_exit:
+ drm_bridge_exit(idx);
}
static void sn65dsi83_atomic_enable(struct drm_bridge *bridge,
@@ -647,6 +699,10 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge,
{
struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
unsigned int pval;
+ int idx;
+
+ if (!drm_bridge_enter(bridge, &idx))
+ return;
/* Clear all errors that got asserted during initialization. */
regmap_read(ctx->regmap, REG_IRQ_STAT, &pval);
@@ -666,32 +722,22 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge,
/* Use the polling task */
sn65dsi83_monitor_start(ctx);
}
+
+ drm_bridge_exit(idx);
}
static void sn65dsi83_atomic_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
- int ret;
+ int idx;
- if (ctx->irq) {
- /* Disable irq */
- regmap_write(ctx->regmap, REG_IRQ_EN, 0x0);
- regmap_write(ctx->regmap, REG_IRQ_GLOBAL, 0x0);
- } else {
- /* Stop the polling task */
- sn65dsi83_monitor_stop(ctx);
- }
-
- /* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */
- gpiod_set_value_cansleep(ctx->enable_gpio, 0);
- usleep_range(10000, 11000);
+ if (!drm_bridge_enter(bridge, &idx))
+ return;
- ret = regulator_disable(ctx->vcc);
- if (ret)
- dev_err(ctx->dev, "Failed to disable vcc: %d\n", ret);
+ devm_release_action(ctx->dev, sn65dsi83_release_resources, ctx);
- regcache_mark_dirty(ctx->regmap);
+ drm_bridge_exit(idx);
}
static enum drm_mode_status
@@ -1012,7 +1058,7 @@ static void sn65dsi83_remove(struct i2c_client *client)
{
struct sn65dsi83 *ctx = i2c_get_clientdata(client);
- drm_bridge_remove(&ctx->bridge);
+ drm_bridge_unplug(&ctx->bridge);
}
static const struct i2c_device_id sn65dsi83_id[] = {
diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c
index b80ee089f880..11b5bb50e9f4 100644
--- a/drivers/gpu/drm/bridge/ti-tfp410.c
+++ b/drivers/gpu/drm/bridge/ti-tfp410.c
@@ -30,7 +30,6 @@ struct tfp410 {
struct gpio_desc *powerdown;
struct drm_bridge_timings timings;
- struct drm_bridge *next_bridge;
struct device *dev;
};
@@ -53,8 +52,8 @@ static int tfp410_get_modes(struct drm_connector *connector)
const struct drm_edid *drm_edid;
int ret;
- if (dvi->next_bridge->ops & DRM_BRIDGE_OP_EDID) {
- drm_edid = drm_bridge_edid_read(dvi->next_bridge, connector);
+ if (dvi->bridge.next_bridge->ops & DRM_BRIDGE_OP_EDID) {
+ drm_edid = drm_bridge_edid_read(dvi->bridge.next_bridge, connector);
if (!drm_edid)
DRM_INFO("EDID read failed. Fallback to standard modes\n");
} else {
@@ -89,7 +88,7 @@ tfp410_connector_detect(struct drm_connector *connector, bool force)
{
struct tfp410 *dvi = drm_connector_to_tfp410(connector);
- return drm_bridge_detect(dvi->next_bridge, connector);
+ return drm_bridge_detect(dvi->bridge.next_bridge, connector);
}
static const struct drm_connector_funcs tfp410_con_funcs = {
@@ -126,7 +125,7 @@ static int tfp410_attach(struct drm_bridge *bridge,
struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
int ret;
- ret = drm_bridge_attach(encoder, dvi->next_bridge, bridge,
+ ret = drm_bridge_attach(encoder, dvi->bridge.next_bridge, bridge,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret < 0)
return ret;
@@ -134,14 +133,14 @@ static int tfp410_attach(struct drm_bridge *bridge,
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
return 0;
- if (dvi->next_bridge->ops & DRM_BRIDGE_OP_DETECT)
+ if (dvi->bridge.next_bridge->ops & DRM_BRIDGE_OP_DETECT)
dvi->connector.polled = DRM_CONNECTOR_POLL_HPD;
else
dvi->connector.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
- if (dvi->next_bridge->ops & DRM_BRIDGE_OP_HPD) {
+ if (dvi->bridge.next_bridge->ops & DRM_BRIDGE_OP_HPD) {
INIT_DELAYED_WORK(&dvi->hpd_work, tfp410_hpd_work_func);
- drm_bridge_hpd_enable(dvi->next_bridge, tfp410_hpd_callback,
+ drm_bridge_hpd_enable(dvi->bridge.next_bridge, tfp410_hpd_callback,
dvi);
}
@@ -149,8 +148,8 @@ static int tfp410_attach(struct drm_bridge *bridge,
&tfp410_con_helper_funcs);
ret = drm_connector_init_with_ddc(bridge->dev, &dvi->connector,
&tfp410_con_funcs,
- dvi->next_bridge->type,
- dvi->next_bridge->ddc);
+ dvi->bridge.next_bridge->type,
+ dvi->bridge.next_bridge->ddc);
if (ret) {
dev_err(dvi->dev, "drm_connector_init_with_ddc() failed: %d\n",
ret);
@@ -169,8 +168,8 @@ static void tfp410_detach(struct drm_bridge *bridge)
{
struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
- if (dvi->connector.dev && dvi->next_bridge->ops & DRM_BRIDGE_OP_HPD) {
- drm_bridge_hpd_disable(dvi->next_bridge);
+ if (dvi->connector.dev && dvi->bridge.next_bridge->ops & DRM_BRIDGE_OP_HPD) {
+ drm_bridge_hpd_disable(dvi->bridge.next_bridge);
cancel_delayed_work_sync(&dvi->hpd_work);
}
}
@@ -362,10 +361,10 @@ static int tfp410_init(struct device *dev, bool i2c)
if (!node)
return -ENODEV;
- dvi->next_bridge = of_drm_find_bridge(node);
+ dvi->bridge.next_bridge = of_drm_find_and_get_bridge(node);
of_node_put(node);
- if (!dvi->next_bridge)
+ if (!dvi->bridge.next_bridge)
return -EPROBE_DEFER;
/* Get the powerdown GPIO. */
diff --git a/drivers/gpu/drm/bridge/ti-tpd12s015.c b/drivers/gpu/drm/bridge/ti-tpd12s015.c
index dcf686c4e73d..136e47ad1a10 100644
--- a/drivers/gpu/drm/bridge/ti-tpd12s015.c
+++ b/drivers/gpu/drm/bridge/ti-tpd12s015.c
@@ -28,8 +28,6 @@ struct tpd12s015_device {
struct gpio_desc *ls_oe_gpio;
struct gpio_desc *hpd_gpio;
int hpd_irq;
-
- struct drm_bridge *next_bridge;
};
static inline struct tpd12s015_device *to_tpd12s015(struct drm_bridge *bridge)
@@ -47,7 +45,7 @@ static int tpd12s015_attach(struct drm_bridge *bridge,
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
return -EINVAL;
- ret = drm_bridge_attach(encoder, tpd->next_bridge,
+ ret = drm_bridge_attach(encoder, tpd->bridge.next_bridge,
bridge, flags);
if (ret < 0)
return ret;
@@ -138,10 +136,10 @@ static int tpd12s015_probe(struct platform_device *pdev)
if (!node)
return -ENODEV;
- tpd->next_bridge = of_drm_find_bridge(node);
+ tpd->bridge.next_bridge = of_drm_find_and_get_bridge(node);
of_node_put(node);
- if (!tpd->next_bridge)
+ if (!tpd->bridge.next_bridge)
return -EPROBE_DEFER;
/* Get the control and HPD GPIOs. */