diff options
author | Daniel Vetter <daniel.vetter@ffwll.ch> | 2022-06-15 20:12:17 +0300 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2022-06-15 20:12:17 +0300 |
commit | 0f95ee9a0c579ebed0309657f6918673927189f2 (patch) | |
tree | e10c39634b67eaaae5d6330f1f3538b24251439c /drivers/gpu/drm | |
parent | b13baccc3850ca8b8cccbf8ed9912dbaa0fdf7f3 (diff) | |
parent | dfa687bffc8a4a21ed929c7dececf01b8f1f52ee (diff) | |
download | linux-0f95ee9a0c579ebed0309657f6918673927189f2.tar.xz |
Merge tag 'drm-misc-next-2022-06-08' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for 5.20:
UAPI Changes:
* connector: export bpc limits in debugfs
* dma-buf: Print buffer name in debugfs
Cross-subsystem Changes:
* dma-buf: Improve dma-fence handling; Cleanups
* fbdev: Device-unregistering fixes
Core Changes:
* client: Only use driver-validated modes to avoid blank screen
* dp-aux: Make probing more reliable; Small fixes
* edit: CEA data-block iterators; Introduce struct drm_edid; Many cleanups
* gem: Don't use framebuffer format's non-exising color planes
* probe-helper: Use 640x480 as DisplayPort fallback; Refactoring
* scheduler: Don't kill jobs in interrupt context
Driver Changes:
* amdgpu: Use atomic fence helpers in DM; Fix VRAM address calculation;
Export CRTC bpc settings via debugfs
* bridge: Add TI-DLPC3433; anx7625: Fixes; fy07024di26a30d: Optional
GPIO reset; icn6211: Cleanups; ldb: Add reg and reg-name properties
to bindings, Kconfig fixes; lt9611: Fix display sensing; lt9611uxc:
Fixes; nwl-dsi: Fixes; ps8640: Cleanups; st7735r: Fixes; tc358767:
DSI/DPI refactoring and DSI-to-eDP support, Fixes; ti-sn65dsi83:
Fixes;
* gma500: Cleanup connector I2C handling
* hyperv: Unify VRAM allocation of Gen1 and Gen2
* i915: export CRTC bpc settings via debugfs
* meson: Support YUV422 output; Refcount fixes
* mgag200: Support damage clipping; Support gamma handling; Protect
concurrent HW access; Fixes to connector; Store model-specific limits
in device-info structure; Cleanups
* nouveau: Fixes and Cleanups
* panel: Kconfig fixes
* panfrost: Valhall support
* r128: Fix bit-shift overflow
* rockchip: Locking fixes in error path; Minor cleanups
* ssd130x: Fix built-in linkage
* ttm: Cleanups
* udl; Always advertize VGA connector
* fbdev/vesa: Support COMPILE_TEST
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
From: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/YqBtumw05JZDEZE2@linux-uq9g
Diffstat (limited to 'drivers/gpu/drm')
85 files changed, 3439 insertions, 2043 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index 49e4092f447f..7a5e8a7b4a1b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -50,6 +50,35 @@ to_amdgpu_device(struct amdgpu_vram_mgr *mgr) return container_of(mgr, struct amdgpu_device, mman.vram_mgr); } +static inline struct drm_buddy_block * +amdgpu_vram_mgr_first_block(struct list_head *list) +{ + return list_first_entry_or_null(list, struct drm_buddy_block, link); +} + +static inline bool amdgpu_is_vram_mgr_blocks_contiguous(struct list_head *head) +{ + struct drm_buddy_block *block; + u64 start, size; + + block = amdgpu_vram_mgr_first_block(head); + if (!block) + return false; + + while (head != block->link.next) { + start = amdgpu_vram_mgr_block_start(block); + size = amdgpu_vram_mgr_block_size(block); + + block = list_entry(block->link.next, struct drm_buddy_block, link); + if (start + size != amdgpu_vram_mgr_block_start(block)) + return false; + } + + return true; +} + + + /** * DOC: mem_info_vram_total * @@ -496,16 +525,22 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man, list_splice_tail(trim_list, &vres->blocks); } - list_for_each_entry(block, &vres->blocks, link) - vis_usage += amdgpu_vram_mgr_vis_size(adev, block); + vres->base.start = 0; + list_for_each_entry(block, &vres->blocks, link) { + unsigned long start; - block = amdgpu_vram_mgr_first_block(&vres->blocks); - if (!block) { - r = -EINVAL; - goto error_fini; - } + start = amdgpu_vram_mgr_block_start(block) + + amdgpu_vram_mgr_block_size(block); + start >>= PAGE_SHIFT; + + if (start > vres->base.num_pages) + start -= vres->base.num_pages; + else + start = 0; + vres->base.start = max(vres->base.start, start); - vres->base.start = amdgpu_vram_mgr_block_start(block) >> PAGE_SHIFT; + vis_usage += amdgpu_vram_mgr_vis_size(adev, block); + } if (amdgpu_is_vram_mgr_blocks_contiguous(&vres->blocks)) vres->base.placement |= TTM_PL_FLAG_CONTIGUOUS; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h index 9a2db87186c7..4b267bf1c5db 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h @@ -53,33 +53,6 @@ static inline u64 amdgpu_vram_mgr_block_size(struct drm_buddy_block *block) return PAGE_SIZE << drm_buddy_block_order(block); } -static inline struct drm_buddy_block * -amdgpu_vram_mgr_first_block(struct list_head *list) -{ - return list_first_entry_or_null(list, struct drm_buddy_block, link); -} - -static inline bool amdgpu_is_vram_mgr_blocks_contiguous(struct list_head *head) -{ - struct drm_buddy_block *block; - u64 start, size; - - block = amdgpu_vram_mgr_first_block(head); - if (!block) - return false; - - while (head != block->link.next) { - start = amdgpu_vram_mgr_block_start(block); - size = amdgpu_vram_mgr_block_size(block); - - block = list_entry(block->link.next, struct drm_buddy_block, link); - if (start + size != amdgpu_vram_mgr_block_start(block)) - return false; - } - - return true; -} - static inline struct amdgpu_vram_mgr_resource * to_amdgpu_vram_mgr_resource(struct ttm_resource *res) { diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 70be67a56673..ad4571190a90 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -83,6 +83,7 @@ #include <drm/drm_edid.h> #include <drm/drm_vblank.h> #include <drm/drm_audio_component.h> +#include <drm/drm_gem_atomic_helper.h> #include "ivsrcid/dcn/irqsrcs_dcn_1_0.h" @@ -6591,14 +6592,12 @@ dm_crtc_duplicate_state(struct drm_crtc *crtc) return &state->base; } -#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY static int amdgpu_dm_crtc_late_register(struct drm_crtc *crtc) { crtc_debugfs_init(crtc); return 0; } -#endif static inline int dm_set_vupdate_irq(struct drm_crtc *crtc, bool enable) { @@ -6692,9 +6691,7 @@ static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = { .enable_vblank = dm_enable_vblank, .disable_vblank = dm_disable_vblank, .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, -#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) .late_register = amdgpu_dm_crtc_late_register, -#endif }; static enum drm_connector_status @@ -7598,6 +7595,10 @@ static int dm_plane_helper_prepare_fb(struct drm_plane *plane, goto error_unpin; } + r = drm_gem_plane_helper_prepare_fb(plane, new_state); + if (unlikely(r != 0)) + goto error_unpin; + amdgpu_bo_unreserve(rbo); afb->address = amdgpu_bo_gpu_offset(rbo); @@ -9132,7 +9133,6 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, struct dm_crtc_state *dm_old_crtc_state = to_dm_crtc_state(drm_atomic_get_old_crtc_state(state, pcrtc)); int planes_count = 0, vpos, hpos; - long r; unsigned long flags; struct amdgpu_bo *abo; uint32_t target_vblank, last_flip_vblank; @@ -9207,18 +9207,6 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, } abo = gem_to_amdgpu_bo(fb->obj[0]); - - /* - * Wait for all fences on this FB. Do limited wait to avoid - * deadlock during GPU reset when this fence will not signal - * but we hold reservation lock for the BO. - */ - r = dma_resv_wait_timeout(abo->tbo.base.resv, - DMA_RESV_USAGE_WRITE, false, - msecs_to_jiffies(5000)); - if (unlikely(r <= 0)) - DRM_ERROR("Waiting for fences timed out!"); - fill_dc_plane_info_and_addr( dm->adev, new_plane_state, afb->tiling_flags, @@ -9561,9 +9549,14 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state; int crtc_disable_count = 0; bool mode_set_reset_required = false; + int r; trace_amdgpu_dm_atomic_commit_tail_begin(state); + r = drm_atomic_helper_wait_for_fences(dev, state, false); + if (unlikely(r)) + DRM_ERROR("Waiting for fences timed out!"); + drm_atomic_helper_update_legacy_modeset_state(dev, state); dm_state = dm_atomic_get_new_state(state); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c index c7a592d68feb..2a78ae007521 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c @@ -871,28 +871,18 @@ static int psr_capability_show(struct seq_file *m, void *data) } /* - * Returns the current and maximum output bpc for the connector. - * Example usage: cat /sys/kernel/debug/dri/0/DP-1/output_bpc + * Returns the current bpc for the crtc. + * Example usage: cat /sys/kernel/debug/dri/0/crtc-0/amdgpu_current_bpc */ -static int output_bpc_show(struct seq_file *m, void *data) +static int amdgpu_current_bpc_show(struct seq_file *m, void *data) { - struct drm_connector *connector = m->private; - struct drm_device *dev = connector->dev; - struct drm_crtc *crtc = NULL; + struct drm_crtc *crtc = m->private; + struct drm_device *dev = crtc->dev; struct dm_crtc_state *dm_crtc_state = NULL; int res = -ENODEV; unsigned int bpc; mutex_lock(&dev->mode_config.mutex); - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - - if (connector->state == NULL) - goto unlock; - - crtc = connector->state->crtc; - if (crtc == NULL) - goto unlock; - drm_modeset_lock(&crtc->mutex, NULL); if (crtc->state == NULL) goto unlock; @@ -922,18 +912,15 @@ static int output_bpc_show(struct seq_file *m, void *data) } seq_printf(m, "Current: %u\n", bpc); - seq_printf(m, "Maximum: %u\n", connector->display_info.bpc); res = 0; unlock: - if (crtc) - drm_modeset_unlock(&crtc->mutex); - - drm_modeset_unlock(&dev->mode_config.connection_mutex); + drm_modeset_unlock(&crtc->mutex); mutex_unlock(&dev->mode_config.mutex); return res; } +DEFINE_SHOW_ATTRIBUTE(amdgpu_current_bpc); /* * Example usage: @@ -2539,7 +2526,6 @@ static int target_backlight_show(struct seq_file *m, void *unused) DEFINE_SHOW_ATTRIBUTE(dp_dsc_fec_support); DEFINE_SHOW_ATTRIBUTE(dmub_fw_state); DEFINE_SHOW_ATTRIBUTE(dmub_tracebuffer); -DEFINE_SHOW_ATTRIBUTE(output_bpc); DEFINE_SHOW_ATTRIBUTE(dp_lttpr_status); #ifdef CONFIG_DRM_AMD_DC_HDCP DEFINE_SHOW_ATTRIBUTE(hdcp_sink_capability); @@ -2786,7 +2772,6 @@ static const struct { const struct file_operations *fops; } connector_debugfs_entries[] = { {"force_yuv420_output", &force_yuv420_output_fops}, - {"output_bpc", &output_bpc_fops}, {"trigger_hotplug", &trigger_hotplug_debugfs_fops}, {"internal_display", &internal_display_fops} }; @@ -3170,9 +3155,10 @@ static int crc_win_update_get(void *data, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(crc_win_update_fops, crc_win_update_get, crc_win_update_set, "%llu\n"); - +#endif void crtc_debugfs_init(struct drm_crtc *crtc) { +#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY struct dentry *dir = debugfs_lookup("crc", crtc->debugfs_entry); if (!dir) @@ -3188,9 +3174,11 @@ void crtc_debugfs_init(struct drm_crtc *crtc) &crc_win_y_end_fops); debugfs_create_file_unsafe("crc_win_update", 0644, dir, crtc, &crc_win_update_fops); - -} #endif + debugfs_create_file("amdgpu_current_bpc", 0644, crtc->debugfs_entry, + crtc, &amdgpu_current_bpc_fops); +} + /* * Writes DTN log state to the user supplied buffer. * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dtn_log diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h index 3366cb644053..071200473c27 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h @@ -31,8 +31,6 @@ void connector_debugfs_init(struct amdgpu_dm_connector *connector); void dtn_debugfs_init(struct amdgpu_device *adev); -#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) void crtc_debugfs_init(struct drm_crtc *crtc); -#endif #endif diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 307b135da2f6..8ffd601e68f9 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -78,6 +78,7 @@ config DRM_DISPLAY_CONNECTOR config DRM_FSL_LDB tristate "Freescale i.MX8MP LDB bridge" depends on OF + depends on ARCH_MXC || COMPILE_TEST select DRM_KMS_HELPER select DRM_PANEL_BRIDGE help @@ -321,6 +322,22 @@ config DRM_TOSHIBA_TC358775 help Toshiba TC358775 DSI/LVDS bridge chip driver. +config DRM_TI_DLPC3433 + tristate "TI DLPC3433 Display controller" + depends on DRM && DRM_PANEL + depends on OF + select DRM_MIPI_DSI + help + TI DLPC3433 is a MIPI DSI based display controller bridge + for processing high resolution DMD based projectors. + + It has a flexible configuration of MIPI DSI and DPI signal + input that produces a DMD output in RGB565, RGB666, RGB888 + formats. + + It supports upto 720p resolution with 60 and 120 Hz refresh + rates. + config DRM_TI_TFP410 tristate "TI TFP410 DVI/HDMI bridge" depends on OF diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index f6c0a95de549..043b499545e3 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o obj-$(CONFIG_DRM_TOSHIBA_TC358768) += tc358768.o obj-$(CONFIG_DRM_TOSHIBA_TC358775) += tc358775.o obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/ +obj-$(CONFIG_DRM_TI_DLPC3433) += ti-dlpc3433.o obj-$(CONFIG_DRM_TI_SN65DSI83) += ti-sn65dsi83.o obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h index 9e3bb8a8ee40..a031a0cd1f18 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511.h +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h @@ -226,18 +226,6 @@ #define ADV7511_REG_CEC_CLK_DIV 0x4e #define ADV7511_REG_CEC_SOFT_RESET 0x50 -static const u8 ADV7511_REG_CEC_RX_FRAME_HDR[] = { - ADV7511_REG_CEC_RX1_FRAME_HDR, - ADV7511_REG_CEC_RX2_FRAME_HDR, - ADV7511_REG_CEC_RX3_FRAME_HDR, -}; - -static const u8 ADV7511_REG_CEC_RX_FRAME_LEN[] = { - ADV7511_REG_CEC_RX1_FRAME_LEN, - ADV7511_REG_CEC_RX2_FRAME_LEN, - ADV7511_REG_CEC_RX3_FRAME_LEN, -}; - #define ADV7533_REG_CEC_OFFSET 0x70 enum adv7511_input_clock { diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c index 399f625a50c8..0b266f28f150 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c @@ -15,6 +15,18 @@ #include "adv7511.h" +static const u8 ADV7511_REG_CEC_RX_FRAME_HDR[] = { + ADV7511_REG_CEC_RX1_FRAME_HDR, + ADV7511_REG_CEC_RX2_FRAME_HDR, + ADV7511_REG_CEC_RX3_FRAME_HDR, +}; + +static const u8 ADV7511_REG_CEC_RX_FRAME_LEN[] = { + ADV7511_REG_CEC_RX1_FRAME_LEN, + ADV7511_REG_CEC_RX2_FRAME_LEN, + ADV7511_REG_CEC_RX3_FRAME_LEN, +}; + #define ADV7511_INT1_CEC_MASK \ (ADV7511_INT1_CEC_TX_READY | ADV7511_INT1_CEC_TX_ARBIT_LOST | \ ADV7511_INT1_CEC_TX_RETRY_TIMEOUT | ADV7511_INT1_CEC_RX_READY1 | \ diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 53a5da6c49dd..e92eb4a40745 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -1638,6 +1638,7 @@ static int anx7625_parse_dt(struct device *dev, bus_type = 0; mipi_lanes = of_property_count_u32_elems(ep0, "data-lanes"); + of_node_put(ep0); } if (bus_type == V4L2_FWNODE_BUS_TYPE_PARALLEL) /* bus type is Parallel(DSI) */ diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c index 829e1a144656..20bece84ff8c 100644 --- a/drivers/gpu/drm/bridge/cdns-dsi.c +++ b/drivers/gpu/drm/bridge/cdns-dsi.c @@ -462,6 +462,7 @@ struct cdns_dsi { struct reset_control *dsi_p_rst; struct clk *dsi_sys_clk; bool link_initialized; + bool phy_initialized; struct phy *dphy; }; @@ -711,11 +712,21 @@ static void cdns_dsi_bridge_disable(struct drm_bridge *bridge) pm_runtime_put(dsi->base.dev); } +static void cdns_dsi_bridge_post_disable(struct drm_bridge *bridge) +{ + struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); + struct cdns_dsi *dsi = input_to_dsi(input); + + pm_runtime_put(dsi->base.dev); +} + static void cdns_dsi_hs_init(struct cdns_dsi *dsi) { struct cdns_dsi_output *output = &dsi->output; u32 status; + if (dsi->phy_initialized) + return; /* * Power all internal DPHY blocks down and maintain their reset line * asserted before changing the DPHY config. @@ -739,6 +750,7 @@ static void cdns_dsi_hs_init(struct cdns_dsi *dsi) writel(DPHY_CMN_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | DPHY_CMN_PDN | DPHY_D_RSTB(output->dev->lanes) | DPHY_C_RSTB, dsi->regs + MCTL_DPHY_CFG0); + dsi->phy_initialized = true; } static void cdns_dsi_init_link(struct cdns_dsi *dsi) @@ -914,11 +926,25 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge) writel(tmp, dsi->regs + MCTL_MAIN_EN); } +static void cdns_dsi_bridge_pre_enable(struct drm_bridge *bridge) +{ + struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); + struct cdns_dsi *dsi = input_to_dsi(input); + + if (WARN_ON(pm_runtime_get_sync(dsi->base.dev) < 0)) + return; + + cdns_dsi_init_link(dsi); + cdns_dsi_hs_init(dsi); +} + static const struct drm_bridge_funcs cdns_dsi_bridge_funcs = { .attach = cdns_dsi_bridge_attach, .mode_valid = cdns_dsi_bridge_mode_valid, .disable = cdns_dsi_bridge_disable, + .pre_enable = cdns_dsi_bridge_pre_enable, .enable = cdns_dsi_bridge_enable, + .post_disable = cdns_dsi_bridge_post_disable, }; static int cdns_dsi_attach(struct mipi_dsi_host *host, diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c index 47dea657a752..2b8cf64cbd80 100644 --- a/drivers/gpu/drm/bridge/chipone-icn6211.c +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c @@ -9,6 +9,8 @@ #include <drm/drm_print.h> #include <drm/drm_mipi_dsi.h> +#include <linux/bitfield.h> +#include <linux/bits.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> @@ -26,6 +28,11 @@ #define PD_CTRL(n) (0x0a + ((n) & 0x3)) /* 0..3 */ #define RST_CTRL(n) (0x0e + ((n) & 0x1)) /* 0..1 */ #define SYS_CTRL(n) (0x10 + ((n) & 0x7)) /* 0..4 */ +#define SYS_CTRL_1_CLK_PHASE_MSK GENMASK(5, 4) +#define CLK_PHASE_0 0 +#define CLK_PHASE_1_4 1 +#define CLK_PHASE_1_2 2 +#define CLK_PHASE_3_4 3 #define RGB_DRV(n) (0x18 + ((n) & 0x3)) /* 0..3 */ #define RGB_DLY(n) (0x1c + ((n) & 0x1)) /* 0..1 */ #define RGB_TEST_CTRL 0x1e @@ -100,7 +107,7 @@ #define MIPI_PN_SWAP 0x87 #define MIPI_PN_SWAP_CLK BIT(4) #define MIPI_PN_SWAP_D(n) BIT((n) & 0x3) -#define MIPI_SOT_SYNC_BIT_(n) (0x88 + ((n) & 0x1)) /* 0..1 */ +#define MIPI_SOT_SYNC_BIT(n) (0x88 + ((n) & 0x1)) /* 0..1 */ #define MIPI_ULPS_CTRL 0x8a #define MIPI_CLK_CHK_VAR 0x8e #define MIPI_CLK_CHK_INI 0x8f @@ -115,7 +122,7 @@ #define MIPI_T_CLK_SETTLE 0x9a #define MIPI_TO_HS_RX_L 0x9e #define MIPI_TO_HS_RX_H 0x9f -#define MIPI_PHY_(n) (0xa0 + ((n) & 0x7)) /* 0..5 */ +#define MIPI_PHY(n) (0xa0 + ((n) & 0x7)) /* 0..5 */ #define MIPI_PD_RX 0xb0 #define MIPI_PD_TERM 0xb1 #define MIPI_PD_HSRX 0xb2 @@ -125,13 +132,11 @@ #define MIPI_FORCE_0 0xb6 #define MIPI_RST_CTRL 0xb7 #define MIPI_RST_NUM 0xb8 -#define MIPI_DBG_SET_(n) (0xc0 + ((n) & 0xf)) /* 0..9 */ +#define MIPI_DBG_SET(n) (0xc0 + ((n) & 0xf)) /* 0..9 */ #define MIPI_DBG_SEL 0xe0 #define MIPI_DBG_DATA 0xe1 #define MIPI_ATE_TEST_SEL 0xe2 -#define MIPI_ATE_STATUS_(n) (0xe3 + ((n) & 0x1)) /* 0..1 */ -#define MIPI_ATE_STATUS_1 0xe4 -#define ICN6211_MAX_REGISTER MIPI_ATE_STATUS(1) +#define MIPI_ATE_STATUS(n) (0xe3 + ((n) & 0x1)) /* 0..1 */ struct chipone { struct device *dev; @@ -155,10 +160,10 @@ static const struct regmap_range chipone_dsi_readable_ranges[] = { regmap_reg_range(MIPI_CLK_CHK_VAR, MIPI_T_TA_SURE_PRE), regmap_reg_range(MIPI_T_LPX_SET, MIPI_INIT_TIME_H), regmap_reg_range(MIPI_T_CLK_TERM_EN, MIPI_T_CLK_SETTLE), - regmap_reg_range(MIPI_TO_HS_RX_L, MIPI_PHY_(5)), + regmap_reg_range(MIPI_TO_HS_RX_L, MIPI_PHY(5)), regmap_reg_range(MIPI_PD_RX, MIPI_RST_NUM), - regmap_reg_range(MIPI_DBG_SET_(0), MIPI_DBG_SET_(9)), - regmap_reg_range(MIPI_DBG_SEL, MIPI_ATE_STATUS_(1)), + regmap_reg_range(MIPI_DBG_SET(0), MIPI_DBG_SET(9)), + regmap_reg_range(MIPI_DBG_SEL, MIPI_ATE_STATUS(1)), }; static const struct regmap_access_table chipone_dsi_readable_table = { @@ -172,10 +177,10 @@ static const struct regmap_range chipone_dsi_writeable_ranges[] = { regmap_reg_range(MIPI_CLK_CHK_VAR, MIPI_T_TA_SURE_PRE), regmap_reg_range(MIPI_T_LPX_SET, MIPI_INIT_TIME_H), regmap_reg_range(MIPI_T_CLK_TERM_EN, MIPI_T_CLK_SETTLE), - regmap_reg_range(MIPI_TO_HS_RX_L, MIPI_PHY_(5)), + regmap_reg_range(MIPI_TO_HS_RX_L, MIPI_PHY(5)), regmap_reg_range(MIPI_PD_RX, MIPI_RST_NUM), - regmap_reg_range(MIPI_DBG_SET_(0), MIPI_DBG_SET_(9)), - regmap_reg_range(MIPI_DBG_SEL, MIPI_ATE_STATUS_(1)), + regmap_reg_range(MIPI_DBG_SET(0), MIPI_DBG_SET(9)), + regmap_reg_range(MIPI_DBG_SEL, MIPI_ATE_STATUS(1)), }; static const struct regmap_access_table chipone_dsi_writeable_table = { @@ -189,7 +194,7 @@ static const struct regmap_config chipone_regmap_config = { .rd_table = &chipone_dsi_readable_table, .wr_table = &chipone_dsi_writeable_table, .cache_type = REGCACHE_RBTREE, - .max_register = MIPI_ATE_STATUS_(1), + .max_register = MIPI_ATE_STATUS(1), }; static int chipone_dsi_read(void *context, @@ -336,7 +341,7 @@ static void chipone_atomic_enable(struct drm_bridge *bridge, const struct drm_bridge_state *bridge_state; u16 hfp, hbp, hsync; u32 bus_flags; - u8 pol, id[4]; + u8 pol, sys_ctrl_1, id[4]; chipone_readb(icn, VENDOR_ID, id); chipone_readb(icn, DEVICE_ID_H, id + 1); @@ -414,7 +419,14 @@ static void chipone_atomic_enable(struct drm_bridge *bridge, chipone_configure_pll(icn, mode); chipone_writeb(icn, SYS_CTRL(0), 0x40); - chipone_writeb(icn, SYS_CTRL(1), 0x88); + sys_ctrl_1 = 0x88; + + if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE) + sys_ctrl_1 |= FIELD_PREP(SYS_CTRL_1_CLK_PHASE_MSK, CLK_PHASE_0); + else + sys_ctrl_1 |= FIELD_PREP(SYS_CTRL_1_CLK_PHASE_MSK, CLK_PHASE_1_2); + + chipone_writeb(icn, SYS_CTRL(1), sys_ctrl_1); /* icn6211 specific sequence */ chipone_writeb(icn, MIPI_FORCE_0, 0x20); diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c index 7ef8fe5abc12..88f2a4f43cfb 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611.c @@ -578,15 +578,13 @@ static struct lt9611_mode *lt9611_find_mode(const struct drm_display_mode *mode) } /* connector funcs */ -static enum drm_connector_status -lt9611_connector_detect(struct drm_connector *connector, bool force) +static enum drm_connector_status __lt9611_detect(struct lt9611 *lt9611) { - struct lt9611 *lt9611 = connector_to_lt9611(connector); unsigned int reg_val = 0; int connected = 0; regmap_read(lt9611->regmap, 0x825e, ®_val); - connected = (reg_val & BIT(0)); + connected = (reg_val & (BIT(2) | BIT(0))); lt9611->status = connected ? connector_status_connected : connector_status_disconnected; @@ -594,6 +592,12 @@ lt9611_connector_detect(struct drm_connector *connector, bool force) return lt9611->status; } +static enum drm_connector_status +lt9611_connector_detect(struct drm_connector *connector, bool force) +{ + return __lt9611_detect(connector_to_lt9611(connector)); +} + static int lt9611_read_edid(struct lt9611 *lt9611) { unsigned int temp; @@ -893,17 +897,7 @@ static void lt9611_bridge_mode_set(struct drm_bridge *bridge, static enum drm_connector_status lt9611_bridge_detect(struct drm_bridge *bridge) { - struct lt9611 *lt9611 = bridge_to_lt9611(bridge); - unsigned int reg_val = 0; - int connected; - - regmap_read(lt9611->regmap, 0x825e, ®_val); - connected = reg_val & BIT(0); - - lt9611->status = connected ? connector_status_connected : - connector_status_disconnected; - - return lt9611->status; + return __lt9611_detect(bridge_to_lt9611(bridge)); } static struct edid *lt9611_bridge_get_edid(struct drm_bridge *bridge, diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c index 3d62e6bf6892..310b3b194491 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c @@ -982,7 +982,7 @@ static int lt9611uxc_remove(struct i2c_client *client) struct lt9611uxc *lt9611uxc = i2c_get_clientdata(client); disable_irq(client->irq); - flush_scheduled_work(); + cancel_work_sync(<9611uxc->work); lt9611uxc_audio_exit(lt9611uxc); drm_bridge_remove(<9611uxc->bridge); diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c index ad74e6558eb3..d83111be4829 100644 --- a/drivers/gpu/drm/bridge/nwl-dsi.c +++ b/drivers/gpu/drm/bridge/nwl-dsi.c @@ -665,6 +665,12 @@ static int nwl_dsi_mode_set(struct nwl_dsi *dsi) return ret; } + ret = phy_set_mode(dsi->phy, PHY_MODE_MIPI_DPHY); + if (ret < 0) { + DRM_DEV_ERROR(dev, "Failed to set DSI phy mode: %d\n", ret); + goto uninit_phy; + } + ret = phy_configure(dsi->phy, phy_cfg); if (ret < 0) { DRM_DEV_ERROR(dev, "Failed to configure DSI phy: %d\n", ret); diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index edb939b14c04..ff4227f6d800 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -537,12 +537,11 @@ static const struct drm_bridge_funcs ps8640_bridge_funcs = { .pre_enable = ps8640_pre_enable, }; -static int ps8640_bridge_host_attach(struct device *dev, struct ps8640 *ps_bridge) +static int ps8640_bridge_get_dsi_resources(struct device *dev, struct ps8640 *ps_bridge) { struct device_node *in_ep, *dsi_node; struct mipi_dsi_device *dsi; struct mipi_dsi_host *host; - int ret; const struct mipi_dsi_device_info info = { .type = "ps8640", .channel = 0, .node = NULL, @@ -577,17 +576,40 @@ static int ps8640_bridge_host_attach(struct device *dev, struct ps8640 *ps_bridg dsi->format = MIPI_DSI_FMT_RGB888; dsi->lanes = NUM_MIPI_LANES; - ret = devm_mipi_dsi_attach(dev, dsi); + return 0; +} + +static int ps8640_bridge_link_panel(struct drm_dp_aux *aux) +{ + struct ps8640 *ps_bridge = aux_to_ps8640(aux); + struct device *dev = aux->dev; + struct device_node *np = dev->of_node; + int ret; + + /* + * NOTE about returning -EPROBE_DEFER from this function: if we + * return an error (most relevant to -EPROBE_DEFER) it will only + * be passed out to ps8640_probe() if it called this directly (AKA the + * panel isn't under the "aux-bus" node). That should be fine because + * if the panel is under "aux-bus" it's guaranteed to have probed by + * the time this function has been called. + */ + + /* port@1 is ps8640 output port */ + ps_bridge->panel_bridge = devm_drm_of_get_bridge(dev, np, 1, 0); + if (IS_ERR(ps_bridge->panel_bridge)) + return PTR_ERR(ps_bridge->panel_bridge); + + ret = devm_drm_bridge_add(dev, &ps_bridge->bridge); if (ret) return ret; - return 0; + return devm_mipi_dsi_attach(dev, ps_bridge->dsi); } static int ps8640_probe(struct i2c_client *client) { struct device *dev = &client->dev; - struct device_node *np = dev->of_node; struct ps8640 *ps_bridge; int ret; u32 i; @@ -628,6 +650,14 @@ static int ps8640_probe(struct i2c_client *client) if (!ps8640_of_panel_on_aux_bus(&client->dev)) ps_bridge->bridge.ops = DRM_BRIDGE_OP_EDID; + /* + * Get MIPI DSI resources early. These can return -EPROBE_DEFER so + * we want to get them out of the way sooner. + */ + ret = ps8640_bridge_get_dsi_resources(&client->dev, ps_bridge); + if (ret) + return ret; + ps_bridge->page[PAGE0_DP_CNTL] = client; ps_bridge->regmap[PAGE0_DP_CNTL] = devm_regmap_init_i2c(client, ps8640_regmap_config); @@ -670,35 +700,19 @@ static int ps8640_probe(struct i2c_client *client) if (ret) return ret; - devm_of_dp_aux_populate_ep_devices(&ps_bridge->aux); - - /* port@1 is ps8640 output port */ - ps_bridge->panel_bridge = devm_drm_of_get_bridge(dev, np, 1, 0); - if (IS_ERR(ps_bridge->panel_bridge)) - return PTR_ERR(ps_bridge->panel_bridge); - - drm_bridge_add(&ps_bridge->bridge); - - ret = ps8640_bridge_host_attach(dev, ps_bridge); - if (ret) - goto err_bridge_remove; + ret = devm_of_dp_aux_populate_bus(&ps_bridge->aux, ps8640_bridge_link_panel); - return 0; + /* + * If devm_of_dp_aux_populate_bus() returns -ENODEV then it's up to + * usa to call ps8640_bridge_link_panel() directly. NOTE: in this case + * the function is allowed to -EPROBE_DEFER. + */ + if (ret == -ENODEV) + return ps8640_bridge_link_panel(&ps_bridge->aux); -err_bridge_remove: - drm_bridge_remove(&ps_bridge->bridge); return ret; } -static int ps8640_remove(struct i2c_client *client) -{ - struct ps8640 *ps_bridge = i2c_get_clientdata(client); - - drm_bridge_remove(&ps_bridge->bridge); - - return 0; -} - static const struct of_device_id ps8640_match[] = { { .compatible = "parade,ps8640" }, { } @@ -707,7 +721,6 @@ MODULE_DEVICE_TABLE(of, ps8640_match); static struct i2c_driver ps8640_driver = { .probe_new = ps8640_probe, - .remove = ps8640_remove, .driver = { .name = "ps8640", .of_match_table = ps8640_match, diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 485717c8f0b4..6c6177028742 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -3,10 +3,7 @@ * TC358767/TC358867/TC9595 DSI/DPI-to-DPI/(e)DP bridge driver * * The TC358767/TC358867/TC9595 can operate in multiple modes. - * The following modes are supported: - * DPI->(e)DP -- supported - * DSI->DPI .... supported - * DSI->(e)DP .. NOT supported + * All modes are supported -- DPI->(e)DP / DSI->DPI / DSI->(e)DP . * * Copyright (C) 2016 CogentEmbedded Inc * Author: Andrey Gusakov <andrey.gusakov@cogentembedded.com> @@ -309,6 +306,9 @@ struct tc_data { /* do we have IRQ */ bool have_irq; + /* Input connector type, DSI and not DPI. */ + bool input_connector_dsi; + /* HPD pin number (0 or 1) or -ENODEV */ int hpd_pin; }; @@ -1247,11 +1247,60 @@ static int tc_main_link_disable(struct tc_data *tc) return regmap_write(tc->regmap, DP0CTL, 0); } -static int tc_dpi_stream_enable(struct tc_data *tc) +static int tc_dsi_rx_enable(struct tc_data *tc) { + u32 value; int ret; + + regmap_write(tc->regmap, PPI_D0S_CLRSIPOCOUNT, 3); + regmap_write(tc->regmap, PPI_D1S_CLRSIPOCOUNT, 3); + regmap_write(tc->regmap, PPI_D2S_CLRSIPOCOUNT, 3); + regmap_write(tc->regmap, PPI_D3S_CLRSIPOCOUNT, 3); + regmap_write(tc->regmap, PPI_D0S_ATMR, 0); + regmap_write(tc->regmap, PPI_D1S_ATMR, 0); + regmap_write(tc->regmap, PPI_TX_RX_TA, TTA_GET | TTA_SURE); + regmap_write(tc->regmap, PPI_LPTXTIMECNT, LPX_PERIOD); + + value = ((LANEENABLE_L0EN << tc->dsi_lanes) - LANEENABLE_L0EN) | + LANEENABLE_CLEN; + regmap_write(tc->regmap, PPI_LANEENABLE, value); + regmap_write(tc->regmap, DSI_LANEENABLE, value); + + /* Set input interface */ + value = DP0_AUDSRC_NO_INPUT; + if (tc_test_pattern) + value |= DP0_VIDSRC_COLOR_BAR; + else + value |= DP0_VIDSRC_DSI_RX; + ret = regmap_write(tc->regmap, SYSCTRL, value); + if (ret) + return ret; + + usleep_range(120, 150); + + regmap_write(tc->regmap, PPI_STARTPPI, PPI_START_FUNCTION); + regmap_write(tc->regmap, DSI_STARTDSI, DSI_RX_START); + + return 0; +} + +static int tc_dpi_rx_enable(struct tc_data *tc) +{ u32 value; + /* Set input interface */ + value = DP0_AUDSRC_NO_INPUT; + if (tc_test_pattern) + value |= DP0_VIDSRC_COLOR_BAR; + else + value |= DP0_VIDSRC_DPI_RX; + return regmap_write(tc->regmap, SYSCTRL, value); +} + +static int tc_dpi_stream_enable(struct tc_data *tc) +{ + int ret; + dev_dbg(tc->dev, "enable video stream\n"); /* Setup PLL */ @@ -1277,20 +1326,6 @@ static int tc_dpi_stream_enable(struct tc_data *tc) if (ret) return ret; - regmap_write(tc->regmap, PPI_D0S_CLRSIPOCOUNT, 3); - regmap_write(tc->regmap, PPI_D1S_CLRSIPOCOUNT, 3); - regmap_write(tc->regmap, PPI_D2S_CLRSIPOCOUNT, 3); - regmap_write(tc->regmap, PPI_D3S_CLRSIPOCOUNT, 3); - regmap_write(tc->regmap, PPI_D0S_ATMR, 0); - regmap_write(tc->regmap, PPI_D1S_ATMR, 0); - regmap_write(tc->regmap, PPI_TX_RX_TA, TTA_GET | TTA_SURE); - regmap_write(tc->regmap, PPI_LPTXTIMECNT, LPX_PERIOD); - - value = ((LANEENABLE_L0EN << tc->dsi_lanes) - LANEENABLE_L0EN) | - LANEENABLE_CLEN; - regmap_write(tc->regmap, PPI_LANEENABLE, value); - regmap_write(tc->regmap, DSI_LANEENABLE, value); - ret = tc_set_common_video_mode(tc, &tc->mode); if (ret) return ret; @@ -1299,22 +1334,7 @@ static int tc_dpi_stream_enable(struct tc_data *tc) if (ret) return ret; - /* Set input interface */ - value = DP0_AUDSRC_NO_INPUT; - if (tc_test_pattern) - value |= DP0_VIDSRC_COLOR_BAR; - else - value |= DP0_VIDSRC_DSI_RX; - ret = regmap_write(tc->regmap, SYSCTRL, value); - if (ret) - return ret; - - usleep_range(120, 150); - - regmap_write(tc->regmap, PPI_STARTPPI, PPI_START_FUNCTION); - regmap_write(tc->regmap, DSI_STARTDSI, DSI_RX_START); - - return 0; + return tc_dsi_rx_enable(tc); } static int tc_dpi_stream_disable(struct tc_data *tc) @@ -1333,8 +1353,18 @@ static int tc_edp_stream_enable(struct tc_data *tc) dev_dbg(tc->dev, "enable video stream\n"); - /* PXL PLL setup */ - if (tc_test_pattern) { + /* + * Pixel PLL must be enabled for DSI input mode and test pattern. + * + * Per TC9595XBG datasheet Revision 0.1 2018-12-27 Figure 4.18 + * "Clock Mode Selection and Clock Sources", either Pixel PLL + * or DPI_PCLK supplies StrmClk. DPI_PCLK is only available in + * case valid Pixel Clock are supplied to the chip DPI input. + * In case built-in test pattern is desired OR DSI input mode + * is used, DPI_PCLK is not available and thus Pixel PLL must + * be used instead. + */ + if (tc->input_connector_dsi || tc_test_pattern) { ret = tc_pxl_pll_en(tc, clk_get_rate(tc->refclk), 1000 * tc->mode.clock); if (ret) @@ -1372,17 +1402,12 @@ static int tc_edp_stream_enable(struct tc_data *tc) ret = regmap_write(tc->regmap, DP0CTL, value); if (ret) return ret; + /* Set input interface */ - value = DP0_AUDSRC_NO_INPUT; - if (tc_test_pattern) - value |= DP0_VIDSRC_COLOR_BAR; + if (tc->input_connector_dsi) + return tc_dsi_rx_enable(tc); else - value |= DP0_VIDSRC_DPI_RX; - ret = regmap_write(tc->regmap, SYSCTRL, value); - if (ret) - return ret; - - return 0; + return tc_dpi_rx_enable(tc); } static int tc_edp_stream_disable(struct tc_data *tc) @@ -1871,7 +1896,7 @@ static int tc_mipi_dsi_host_attach(struct tc_data *tc) of_node_put(host_node); of_node_put(endpoint); - if (dsi_lanes < 0 || dsi_lanes > 4) + if (dsi_lanes <= 0 || dsi_lanes > 4) return -EINVAL; if (!host) @@ -1992,18 +2017,29 @@ static int tc_probe_bridge_endpoint(struct tc_data *tc) mode |= BIT(endpoint.port); } - if (mode == mode_dpi_to_edp || mode == mode_dpi_to_dp) + if (mode == mode_dpi_to_edp || mode == mode_dpi_to_dp) { + tc->input_connector_dsi = false; return tc_probe_edp_bridge_endpoint(tc); - else if (mode == mode_dsi_to_dpi) + } else if (mode == mode_dsi_to_dpi) { + tc->input_connector_dsi = true; return tc_probe_dpi_bridge_endpoint(tc); - else if (mode == mode_dsi_to_edp || mode == mode_dsi_to_dp) - dev_warn(dev, "The mode DSI-to-(e)DP is not supported!\n"); - else - dev_warn(dev, "Invalid mode (0x%x) is not supported!\n", mode); + } else if (mode == mode_dsi_to_edp || mode == mode_dsi_to_dp) { + tc->input_connector_dsi = true; + return tc_probe_edp_bridge_endpoint(tc); + } + + dev_warn(dev, "Invalid mode (0x%x) is not supported!\n", mode); return -EINVAL; } +static void tc_clk_disable(void *data) +{ + struct clk *refclk = data; + + clk_disable_unprepare(refclk); +} + static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; @@ -2020,6 +2056,24 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) if (ret) return ret; + tc->refclk = devm_clk_get(dev, "ref"); + if (IS_ERR(tc->refclk)) { + ret = PTR_ERR(tc->refclk); + dev_err(dev, "Failed to get refclk: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(tc->refclk); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, tc_clk_disable, tc->refclk); + if (ret) + return ret; + + /* tRSTW = 100 cycles , at 13 MHz that is ~7.69 us */ + usleep_range(10, 15); + /* Shut down GPIO is optional */ tc->sd_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); if (IS_ERR(tc->sd_gpio)) @@ -2040,13 +2094,6 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) usleep_range(5000, 10000); } - tc->refclk = devm_clk_get(dev, "ref"); - if (IS_ERR(tc->refclk)) { - ret = PTR_ERR(tc->refclk); - dev_err(dev, "Failed to get refclk: %d\n", ret); - return ret; - } - tc->regmap = devm_regmap_init_i2c(client, &tc_regmap_config); if (IS_ERR(tc->regmap)) { ret = PTR_ERR(tc->regmap); @@ -2137,7 +2184,7 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) i2c_set_clientdata(client, tc); - if (tc->bridge.type == DRM_MODE_CONNECTOR_DPI) { /* DPI output */ + if (tc->input_connector_dsi) { /* DSI input */ ret = tc_mipi_dsi_host_attach(tc); if (ret) { drm_bridge_remove(&tc->bridge); diff --git a/drivers/gpu/drm/bridge/ti-dlpc3433.c b/drivers/gpu/drm/bridge/ti-dlpc3433.c new file mode 100644 index 000000000000..06e519798ac5 --- /dev/null +++ b/drivers/gpu/drm/bridge/ti-dlpc3433.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 RenewOutReach + * Copyright (C) 2021 Amarula Solutions(India) + * + * Author: + * Jagan Teki <jagan@amarulasolutions.com> + * Christopher Vollo <chris@renewoutreach.org> + */ + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_of.h> +#include <drm/drm_print.h> +#include <drm/drm_mipi_dsi.h> + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +enum cmd_registers { + WR_INPUT_SOURCE = 0x05, /* Write Input Source Select */ + WR_EXT_SOURCE_FMT = 0x07, /* Write External Video Source Format */ + WR_IMAGE_CROP = 0x10, /* Write Image Crop */ + WR_DISPLAY_SIZE = 0x12, /* Write Display Size */ + WR_IMAGE_FREEZE = 0x1A, /* Write Image Freeze */ + WR_INPUT_IMAGE_SIZE = 0x2E, /* Write External Input Image Size */ + WR_RGB_LED_EN = 0x52, /* Write RGB LED Enable */ + WR_RGB_LED_CURRENT = 0x54, /* Write RGB LED Current */ + WR_RGB_LED_MAX_CURRENT = 0x5C, /* Write RGB LED Max Current */ + WR_DSI_HS_CLK = 0xBD, /* Write DSI HS Clock */ + RD_DEVICE_ID = 0xD4, /* Read Controller Device ID */ + WR_DSI_PORT_EN = 0xD7, /* Write DSI Port Enable */ +}; + +enum input_source { + INPUT_EXTERNAL_VIDEO = 0, + INPUT_TEST_PATTERN, + INPUT_SPLASH_SCREEN, +}; + +#define DEV_ID_MASK GENMASK(3, 0) +#define IMAGE_FREESE_EN BIT(0) +#define DSI_PORT_EN 0 +#define EXT_SOURCE_FMT_DSI 0 +#define RED_LED_EN BIT(0) +#define GREEN_LED_EN BIT(1) +#define BLUE_LED_EN BIT(2) +#define LED_MASK GENMASK(2, 0) +#define MAX_BYTE_SIZE 8 + +struct dlpc { + struct device *dev; + struct drm_bridge bridge; + struct drm_bridge *next_bridge; + struct device_node *host_node; + struct mipi_dsi_device *dsi; + struct drm_display_mode mode; + + struct gpio_desc *enable_gpio; + struct regulator *vcc_intf; + struct regulator *vcc_flsh; + struct regmap *regmap; + unsigned int dsi_lanes; +}; + +static inline struct dlpc *bridge_to_dlpc(struct drm_bridge *bridge) +{ + return container_of(bridge, struct dlpc, bridge); +} + +static bool dlpc_writeable_noinc_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WR_IMAGE_CROP: + case WR_DISPLAY_SIZE: + case WR_INPUT_IMAGE_SIZE: + case WR_DSI_HS_CLK: + return true; + default: + return false; + } +} + +static const struct regmap_range dlpc_volatile_ranges[] = { + { .range_min = 0x10, .range_max = 0xBF }, +}; + +static const struct regmap_access_table dlpc_volatile_table = { + .yes_ranges = dlpc_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(dlpc_volatile_ranges), +}; + +static struct regmap_config dlpc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = WR_DSI_PORT_EN, + .writeable_noinc_reg = dlpc_writeable_noinc_reg, + .volatile_table = &dlpc_volatile_table, + .cache_type = REGCACHE_RBTREE, + .name = "dlpc3433", +}; + +static void dlpc_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct dlpc *dlpc = bridge_to_dlpc(bridge); + struct device *dev = dlpc->dev; + struct drm_display_mode *mode = &dlpc->mode; + struct regmap *regmap = dlpc->regmap; + char buf[MAX_BYTE_SIZE]; + unsigned int devid; + + regmap_read(regmap, RD_DEVICE_ID, &devid); + devid &= DEV_ID_MASK; + + DRM_DEV_DEBUG(dev, "DLPC3433 device id: 0x%02x\n", devid); + + if (devid != 0x01) { + DRM_DEV_ERROR(dev, "Unsupported DLPC device id: 0x%02x\n", devid); + return; + } + + /* disable image freeze */ + regmap_write(regmap, WR_IMAGE_FREEZE, IMAGE_FREESE_EN); + + /* enable DSI port */ + regmap_write(regmap, WR_DSI_PORT_EN, DSI_PORT_EN); + + memset(buf, 0, MAX_BYTE_SIZE); + + /* set image crop */ + buf[4] = mode->hdisplay & 0xff; + buf[5] = (mode->hdisplay & 0xff00) >> 8; + buf[6] = mode->vdisplay & 0xff; + buf[7] = (mode->vdisplay & 0xff00) >> 8; + regmap_noinc_write(regmap, WR_IMAGE_CROP, buf, MAX_BYTE_SIZE); + + /* set display size */ + buf[4] = mode->hdisplay & 0xff; + buf[5] = (mode->hdisplay & 0xff00) >> 8; + buf[6] = mode->vdisplay & 0xff; + buf[7] = (mode->vdisplay & 0xff00) >> 8; + regmap_noinc_write(regmap, WR_DISPLAY_SIZE, buf, MAX_BYTE_SIZE); + + /* set input image size */ + buf[0] = mode->hdisplay & 0xff; + buf[1] = (mode->hdisplay & 0xff00) >> 8; + buf[2] = mode->vdisplay & 0xff; + buf[3] = (mode->vdisplay & 0xff00) >> 8; + regmap_noinc_write(regmap, WR_INPUT_IMAGE_SIZE, buf, 4); + + /* set external video port */ + regmap_write(regmap, WR_INPUT_SOURCE, INPUT_EXTERNAL_VIDEO); + + /* set external video format select as DSI */ + regmap_write(regmap, WR_EXT_SOURCE_FMT, EXT_SOURCE_FMT_DSI); + + /* disable image freeze */ + regmap_write(regmap, WR_IMAGE_FREEZE, 0x00); + + /* enable RGB led */ + regmap_update_bits(regmap, WR_RGB_LED_EN, LED_MASK, + RED_LED_EN | GREEN_LED_EN | BLUE_LED_EN); + + msleep(10); +} + +static void dlpc_atomic_pre_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct dlpc *dlpc = bridge_to_dlpc(bridge); + int ret; + + gpiod_set_value(dlpc->enable_gpio, 1); + + msleep(500); + + ret = regulator_enable(dlpc->vcc_intf); + if (ret) + DRM_DEV_ERROR(dlpc->dev, + "failed to enable VCC_INTF regulator: %d\n", ret); + + ret = regulator_enable(dlpc->vcc_flsh); + if (ret) + DRM_DEV_ERROR(dlpc->dev, + "failed to enable VCC_FLSH regulator: %d\n", ret); + + msleep(10); +} + +static void dlpc_atomic_post_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct dlpc *dlpc = bridge_to_dlpc(bridge); + + regulator_disable(dlpc->vcc_flsh); + regulator_disable(dlpc->vcc_intf); + + msleep(10); + + gpiod_set_value(dlpc->enable_gpio, 0); + + msleep(500); +} + +#define MAX_INPUT_SEL_FORMATS 1 + +static u32 * +dlpc_atomic_get_input_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + u32 *input_fmts; + + *num_input_fmts = 0; + + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts), + GFP_KERNEL); + if (!input_fmts) + return NULL; + + /* This is the DSI-end bus format */ + input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24; + *num_input_fmts = 1; + + return input_fmts; +} + +static void dlpc_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct dlpc *dlpc = bridge_to_dlpc(bridge); + + drm_mode_copy(&dlpc->mode, adjusted_mode); +} + +static int dlpc_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct dlpc *dlpc = bridge_to_dlpc(bridge); + + return drm_bridge_attach(bridge->encoder, dlpc->next_bridge, bridge, flags); +} + +static const struct drm_bridge_funcs dlpc_bridge_funcs = { + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_get_input_bus_fmts = dlpc_atomic_get_input_bus_fmts, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_pre_enable = dlpc_atomic_pre_enable, + .atomic_enable = dlpc_atomic_enable, + .atomic_post_disable = dlpc_atomic_post_disable, + .mode_set = dlpc_mode_set, + .attach = dlpc_attach, +}; + +static int dlpc3433_parse_dt(struct dlpc *dlpc) +{ + struct device *dev = dlpc->dev; + struct device_node *endpoint; + int ret; + + dlpc->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(dlpc->enable_gpio)) + return PTR_ERR(dlpc->enable_gpio); + + dlpc->vcc_intf = devm_regulator_get(dlpc->dev, "vcc_intf"); + if (IS_ERR(dlpc->vcc_intf)) + return dev_err_probe(dev, PTR_ERR(dlpc->vcc_intf), + "failed to get VCC_INTF supply\n"); + + dlpc->vcc_flsh = devm_regulator_get(dlpc->dev, "vcc_flsh"); + if (IS_ERR(dlpc->vcc_flsh)) + return dev_err_probe(dev, PTR_ERR(dlpc->vcc_flsh), + "failed to get VCC_FLSH supply\n"); + + dlpc->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); + if (IS_ERR(dlpc->next_bridge)) + return PTR_ERR(dlpc->next_bridge); + + endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0); + dlpc->dsi_lanes = of_property_count_u32_elems(endpoint, "data-lanes"); + if (dlpc->dsi_lanes < 0 || dlpc->dsi_lanes > 4) { + ret = -EINVAL; + goto err_put_endpoint; + } + + dlpc->host_node = of_graph_get_remote_port_parent(endpoint); + if (!dlpc->host_node) { + ret = -ENODEV; + goto err_put_host; + } + + of_node_put(endpoint); + + return 0; + +err_put_host: + of_node_put(dlpc->host_node); +err_put_endpoint: + of_node_put(endpoint); + return ret; +} + +static int dlpc_host_attach(struct dlpc *dlpc) +{ + struct device *dev = dlpc->dev; + struct mipi_dsi_host *host; + struct mipi_dsi_device_info info = { + .type = "dlpc3433", + .channel = 0, + .node = NULL, + }; + + host = of_find_mipi_dsi_host_by_node(dlpc->host_node); + if (!host) { + DRM_DEV_ERROR(dev, "failed to find dsi host\n"); + return -EPROBE_DEFER; + } + + dlpc->dsi = mipi_dsi_device_register_full(host, &info); + if (IS_ERR(dlpc->dsi)) { + DRM_DEV_ERROR(dev, "failed to create dsi device\n"); + return PTR_ERR(dlpc->dsi); + } + + dlpc->dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST; + dlpc->dsi->format = MIPI_DSI_FMT_RGB565; + dlpc->dsi->lanes = dlpc->dsi_lanes; + + return devm_mipi_dsi_attach(dev, dlpc->dsi); +} + +static int dlpc3433_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct dlpc *dlpc; + int ret; + + dlpc = devm_kzalloc(dev, sizeof(*dlpc), GFP_KERNEL); + if (!dlpc) + return -ENOMEM; + + dlpc->dev = dev; + + dlpc->regmap = devm_regmap_init_i2c(client, &dlpc_regmap_config); + if (IS_ERR(dlpc->regmap)) + return PTR_ERR(dlpc->regmap); + + ret = dlpc3433_parse_dt(dlpc); + if (ret) + return ret; + + dev_set_drvdata(dev, dlpc); + i2c_set_clientdata(client, dlpc); + + dlpc->bridge.funcs = &dlpc_bridge_funcs; + dlpc->bridge.of_node = dev->of_node; + drm_bridge_add(&dlpc->bridge); + + ret = dlpc_host_attach(dlpc); + if (ret) { + DRM_DEV_ERROR(dev, "failed to attach dsi host\n"); + goto err_remove_bridge; + } + + return 0; + +err_remove_bridge: + drm_bridge_remove(&dlpc->bridge); + return ret; +} + +static int dlpc3433_remove(struct i2c_client *client) +{ + struct dlpc *dlpc = i2c_get_clientdata(client); + + drm_bridge_remove(&dlpc->bridge); + of_node_put(dlpc->host_node); + + return 0; +} + +static const struct i2c_device_id dlpc3433_id[] = { + { "ti,dlpc3433", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, dlpc3433_id); + +static const struct of_device_id dlpc3433_match_table[] = { + { .compatible = "ti,dlpc3433" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dlpc3433_match_table); + +static struct i2c_driver dlpc3433_driver = { + .probe_new = dlpc3433_probe, + .remove = dlpc3433_remove, + .id_table = dlpc3433_id, + .driver = { + .name = "ti-dlpc3433", + .of_match_table = dlpc3433_match_table, + }, +}; +module_i2c_driver(dlpc3433_driver); + +MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>"); +MODULE_AUTHOR("Christopher Vollo <chris@renewoutreach.org>"); +MODULE_DESCRIPTION("TI DLPC3433 MIPI DSI Display Controller Bridge"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/display/drm_dp_aux_bus.c b/drivers/gpu/drm/display/drm_dp_aux_bus.c index dccf3e2ea323..f5741b45ca07 100644 --- a/drivers/gpu/drm/display/drm_dp_aux_bus.c +++ b/drivers/gpu/drm/display/drm_dp_aux_bus.c @@ -3,10 +3,10 @@ * Copyright 2021 Google Inc. * * The DP AUX bus is used for devices that are connected over a DisplayPort - * AUX bus. The devices on the far side of the bus are referred to as - * endpoints in this code. + * AUX bus. The device on the far side of the bus is referred to as an + * endpoint in this code. * - * Commonly there is only one device connected to the DP AUX bus: a panel. + * There is only one device connected to the DP AUX bus: an eDP panel. * Though historically panels (even DP panels) have been modeled as simple * platform devices, putting them under the DP AUX bus allows the panel driver * to perform transactions on that bus. @@ -22,6 +22,11 @@ #include <drm/display/drm_dp_aux_bus.h> #include <drm/display/drm_dp_helper.h> +struct dp_aux_ep_device_with_data { + struct dp_aux_ep_device aux_ep; + int (*done_probing)(struct drm_dp_aux *aux); +}; + /** * dp_aux_ep_match() - The match function for the dp_aux_bus. * @dev: The device to match. @@ -48,6 +53,8 @@ static int dp_aux_ep_probe(struct device *dev) { struct dp_aux_ep_driver *aux_ep_drv = to_dp_aux_ep_drv(dev->driver); struct dp_aux_ep_device *aux_ep = to_dp_aux_ep_dev(dev); + struct dp_aux_ep_device_with_data *aux_ep_with_data = + container_of(aux_ep, struct dp_aux_ep_device_with_data, aux_ep); int ret; ret = dev_pm_domain_attach(dev, true); @@ -56,7 +63,32 @@ static int dp_aux_ep_probe(struct device *dev) ret = aux_ep_drv->probe(aux_ep); if (ret) - dev_pm_domain_detach(dev, true); + goto err_attached; + + if (aux_ep_with_data->done_probing) { + ret = aux_ep_with_data->done_probing(aux_ep->aux); + if (ret) { + /* + * The done_probing() callback should not return + * -EPROBE_DEFER to us. If it does, we treat it as an + * error. Passing it on as-is would cause the _panel_ + * to defer. + */ + if (ret == -EPROBE_DEFER) { + dev_err(dev, + "DP AUX done_probing() can't defer\n"); + ret = -EINVAL; + } + goto err_probed; + } + } + + return 0; +err_probed: + if (aux_ep_drv->remove) + aux_ep_drv->remove(aux_ep); +err_attached: + dev_pm_domain_detach(dev, true); return ret; } @@ -66,7 +98,6 @@ static int dp_aux_ep_probe(struct device *dev) * @dev: The device to remove. * * Calls through to the endpoint driver remove. - * */ static void dp_aux_ep_remove(struct device *dev) { @@ -120,12 +151,14 @@ ATTRIBUTE_GROUPS(dp_aux_ep_dev); /** * dp_aux_ep_dev_release() - Free memory for the dp_aux_ep device * @dev: The device to free. - * - * Return: 0 if no error or negative error code. */ static void dp_aux_ep_dev_release(struct device *dev) { - kfree(to_dp_aux_ep_dev(dev)); + struct dp_aux_ep_device *aux_ep = to_dp_aux_ep_dev(dev); + struct dp_aux_ep_device_with_data *aux_ep_with_data = + container_of(aux_ep, struct dp_aux_ep_device_with_data, aux_ep); + + kfree(aux_ep_with_data); } static struct device_type dp_aux_device_type_type = { @@ -139,12 +172,14 @@ static struct device_type dp_aux_device_type_type = { * @dev: The device to destroy. * @data: Not used * - * This is just used as a callback by of_dp_aux_depopulate_ep_devices() and + * This is just used as a callback by of_dp_aux_depopulate_bus() and * is called for _all_ of the child devices of the device providing the AUX bus. * We'll only act on those that are of type "dp_aux_bus_type". * - * This function is effectively an inverse of what's in the loop - * in of_dp_aux_populate_ep_devices(). + * This function is effectively an inverse of what's in + * of_dp_aux_populate_bus(). NOTE: since we only populate one child + * then it's expected that only one device will match all the "if" tests in + * this function and get to the device_unregister(). * * Return: 0 if no error or negative error code. */ @@ -167,122 +202,150 @@ static int of_dp_aux_ep_destroy(struct device *dev, void *data) } /** - * of_dp_aux_depopulate_ep_devices() - Undo of_dp_aux_populate_ep_devices - * @aux: The AUX channel whose devices we want to depopulate + * of_dp_aux_depopulate_bus() - Undo of_dp_aux_populate_bus + * @aux: The AUX channel whose device we want to depopulate * - * This will destroy all devices that were created - * by of_dp_aux_populate_ep_devices(). + * This will destroy the device that was created + * by of_dp_aux_populate_bus(). */ -void of_dp_aux_depopulate_ep_devices(struct drm_dp_aux *aux) +void of_dp_aux_depopulate_bus(struct drm_dp_aux *aux) { device_for_each_child_reverse(aux->dev, NULL, of_dp_aux_ep_destroy); } -EXPORT_SYMBOL_GPL(of_dp_aux_depopulate_ep_devices); +EXPORT_SYMBOL_GPL(of_dp_aux_depopulate_bus); /** - * of_dp_aux_populate_ep_devices() - Populate the endpoint devices on the DP AUX - * @aux: The AUX channel whose devices we want to populate. It is required that + * of_dp_aux_populate_bus() - Populate the endpoint device on the DP AUX + * @aux: The AUX channel whose device we want to populate. It is required that * drm_dp_aux_init() has already been called for this AUX channel. + * @done_probing: Callback functions to call after EP device finishes probing. + * Will not be called if there are no EP devices and this + * function will return -ENODEV. * - * This will populate all the devices under the "aux-bus" node of the device - * providing the AUX channel (AKA aux->dev). + * This will populate the device (expected to be an eDP panel) under the + * "aux-bus" node of the device providing the AUX channel (AKA aux->dev). * * When this function finishes, it is _possible_ (but not guaranteed) that - * our sub-devices will have finished probing. It should be noted that if our - * sub-devices return -EPROBE_DEFER that we will not return any error codes - * ourselves but our sub-devices will _not_ have actually probed successfully - * yet. There may be other cases (maybe added in the future?) where sub-devices - * won't have been probed yet when this function returns, so it's best not to - * rely on that. + * our sub-device will have finished probing. It should be noted that if our + * sub-device returns -EPROBE_DEFER or is probing asynchronously for some + * reason that we will not return any error codes ourselves but our + * sub-device will _not_ have actually probed successfully yet. + * + * In many cases it's important for the caller of this function to be notified + * when our sub device finishes probing. Our sub device is expected to be an + * eDP panel and the caller is expected to be an eDP controller. The eDP + * controller needs to be able to get a reference to the panel when it finishes + * probing. For this reason the caller can pass in a function pointer that + * will be called when our sub-device finishes probing. * * If this function succeeds you should later make sure you call - * of_dp_aux_depopulate_ep_devices() to undo it, or just use the devm version + * of_dp_aux_depopulate_bus() to undo it, or just use the devm version * of this function. * - * Return: 0 if no error or negative error code. + * Return: 0 if no error or negative error code; returns -ENODEV if there are + * no children. The done_probing() function won't be called in that + * case. */ -int of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux) +int of_dp_aux_populate_bus(struct drm_dp_aux *aux, + int (*done_probing)(struct drm_dp_aux *aux)) { - struct device_node *bus, *np; + struct device_node *bus = NULL, *np = NULL; struct dp_aux_ep_device *aux_ep; + struct dp_aux_ep_device_with_data *aux_ep_with_data; int ret; /* drm_dp_aux_init() should have been called already; warn if not */ WARN_ON_ONCE(!aux->ddc.algo); if (!aux->dev->of_node) - return 0; - + return -ENODEV; bus = of_get_child_by_name(aux->dev->of_node, "aux-bus"); if (!bus) - return 0; + return -ENODEV; - for_each_available_child_of_node(bus, np) { - if (of_node_test_and_set_flag(np, OF_POPULATED)) - continue; + np = of_get_next_available_child(bus, NULL); + of_node_put(bus); + if (!np) + return -ENODEV; - aux_ep = kzalloc(sizeof(*aux_ep), GFP_KERNEL); - if (!aux_ep) - continue; - aux_ep->aux = aux; + if (of_node_test_and_set_flag(np, OF_POPULATED)) { + dev_err(aux->dev, "DP AUX EP device already populated\n"); + ret = -EINVAL; + goto err_did_get_np; + } - aux_ep->dev.parent = aux->dev; - aux_ep->dev.bus = &dp_aux_bus_type; - aux_ep->dev.type = &dp_aux_device_type_type; - aux_ep->dev.of_node = of_node_get(np); - dev_set_name(&aux_ep->dev, "aux-%s", dev_name(aux->dev)); + aux_ep_with_data = kzalloc(sizeof(*aux_ep_with_data), GFP_KERNEL); + if (!aux_ep_with_data) { + ret = -ENOMEM; + goto err_did_set_populated; + } - ret = device_register(&aux_ep->dev); - if (ret) { - dev_err(aux->dev, "Failed to create AUX EP for %pOF: %d\n", np, ret); - of_node_clear_flag(np, OF_POPULATED); - of_node_put(np); + aux_ep_with_data->done_probing = done_probing; - /* - * As per docs of device_register(), call this instead - * of kfree() directly for error cases. - */ - put_device(&aux_ep->dev); + aux_ep = &aux_ep_with_data->aux_ep; + aux_ep->aux = aux; + aux_ep->dev.parent = aux->dev; + aux_ep->dev.bus = &dp_aux_bus_type; + aux_ep->dev.type = &dp_aux_device_type_type; + aux_ep->dev.of_node = of_node_get(np); + dev_set_name(&aux_ep->dev, "aux-%s", dev_name(aux->dev)); - /* - * Following in the footsteps of of_i2c_register_devices(), - * we won't fail the whole function here--we'll just - * continue registering any other devices we find. - */ - } - } + ret = device_register(&aux_ep->dev); + if (ret) { + dev_err(aux->dev, "Failed to create AUX EP for %pOF: %d\n", np, ret); - of_node_put(bus); + /* + * As per docs of device_register(), call this instead + * of kfree() directly for error cases. + */ + put_device(&aux_ep->dev); + + goto err_did_set_populated; + } return 0; + +err_did_set_populated: + of_node_clear_flag(np, OF_POPULATED); + +err_did_get_np: + of_node_put(np); + + return ret; } +EXPORT_SYMBOL_GPL(of_dp_aux_populate_bus); -static void of_dp_aux_depopulate_ep_devices_void(void *data) +static void of_dp_aux_depopulate_bus_void(void *data) { - of_dp_aux_depopulate_ep_devices(data); + of_dp_aux_depopulate_bus(data); } /** - * devm_of_dp_aux_populate_ep_devices() - devm wrapper for of_dp_aux_populate_ep_devices() - * @aux: The AUX channel whose devices we want to populate + * devm_of_dp_aux_populate_bus() - devm wrapper for of_dp_aux_populate_bus() + * @aux: The AUX channel whose device we want to populate + * @done_probing: Callback functions to call after EP device finishes probing. + * Will not be called if there are no EP devices and this + * function will return -ENODEV. * * Handles freeing w/ devm on the device "aux->dev". * - * Return: 0 if no error or negative error code. + * Return: 0 if no error or negative error code; returns -ENODEV if there are + * no children. The done_probing() function won't be called in that + * case. */ -int devm_of_dp_aux_populate_ep_devices(struct drm_dp_aux *aux) +int devm_of_dp_aux_populate_bus(struct drm_dp_aux *aux, + int (*done_probing)(struct drm_dp_aux *aux)) { int ret; - ret = of_dp_aux_populate_ep_devices(aux); + ret = of_dp_aux_populate_bus(aux, done_probing); if (ret) return ret; return devm_add_action_or_reset(aux->dev, - of_dp_aux_depopulate_ep_devices_void, - aux); + of_dp_aux_depopulate_bus_void, aux); } -EXPORT_SYMBOL_GPL(devm_of_dp_aux_populate_ep_devices); +EXPORT_SYMBOL_GPL(devm_of_dp_aux_populate_bus); int __dp_aux_dp_driver_register(struct dp_aux_ep_driver *drv, struct module *owner) { diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index c96847fc0ebc..e275b4ca344b 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -170,6 +170,29 @@ void drm_bridge_add(struct drm_bridge *bridge) } EXPORT_SYMBOL(drm_bridge_add); +static void drm_bridge_remove_void(void *bridge) +{ + drm_bridge_remove(bridge); +} + +/** + * devm_drm_bridge_add - devm managed version of drm_bridge_add() + * + * @dev: device to tie the bridge lifetime to + * @bridge: bridge control structure + * + * This is the managed version of drm_bridge_add() which automatically + * calls drm_bridge_remove() when @dev is unbound. + * + * Return: 0 if no error or negative error code. + */ +int devm_drm_bridge_add(struct device *dev, struct drm_bridge *bridge) +{ + drm_bridge_add(bridge); + return devm_add_action_or_reset(dev, drm_bridge_remove_void, bridge); +} +EXPORT_SYMBOL(devm_drm_bridge_add); + /** * drm_bridge_remove - remove the given bridge from the global bridge list * diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index e6346a67cd98..48e6ce16439f 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -158,22 +158,31 @@ drm_connector_has_preferred_mode(struct drm_connector *connector, int width, int return NULL; } -static struct drm_display_mode * -drm_connector_pick_cmdline_mode(struct drm_connector *connector) +static struct drm_display_mode *drm_connector_pick_cmdline_mode(struct drm_connector *connector) { struct drm_cmdline_mode *cmdline_mode; struct drm_display_mode *mode; bool prefer_non_interlace; + /* + * Find a user-defined mode. If the user gave us a valid + * mode on the kernel command line, it will show up in this + * list. + */ + + list_for_each_entry(mode, &connector->modes, head) { + if (mode->type & DRM_MODE_TYPE_USERDEF) + return mode; + } + cmdline_mode = &connector->cmdline_mode; if (cmdline_mode->specified == false) return NULL; - /* attempt to find a matching mode in the list of modes - * we have gotten so far, if not add a CVT mode that conforms + /* + * Attempt to find a matching mode in the list of modes we + * have gotten so far. */ - if (cmdline_mode->rb || cmdline_mode->margins) - goto create_mode; prefer_non_interlace = !cmdline_mode->interlace; again: @@ -207,12 +216,7 @@ again: goto again; } -create_mode: - mode = drm_mode_create_from_cmdline_mode(connector->dev, cmdline_mode); - if (mode) - list_add(&mode->head, &connector->modes); - - return mode; + return NULL; } static bool drm_connector_enabled(struct drm_connector *connector, bool strict) diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 7f1b82dbaebb..fb04b7a984de 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -395,6 +395,23 @@ static int vrr_range_show(struct seq_file *m, void *data) } DEFINE_SHOW_ATTRIBUTE(vrr_range); +/* + * Returns Connector's max supported bpc through debugfs file. + * Example usage: cat /sys/kernel/debug/dri/0/DP-1/output_bpc + */ +static int output_bpc_show(struct seq_file *m, void *data) +{ + struct drm_connector *connector = m->private; + + if (connector->status != connector_status_connected) + return -ENODEV; + + seq_printf(m, "Maximum: %u\n", connector->display_info.bpc); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(output_bpc); + static const struct file_operations drm_edid_fops = { .owner = THIS_MODULE, .open = edid_open, @@ -437,6 +454,10 @@ void drm_debugfs_connector_add(struct drm_connector *connector) debugfs_create_file("vrr_range", S_IRUGO, root, connector, &vrr_range_fops); + /* max bpc */ + debugfs_create_file("output_bpc", 0444, root, connector, + &output_bpc_fops); + if (connector->funcs->debugfs_init) connector->funcs->debugfs_init(connector, root); } diff --git a/drivers/gpu/drm/drm_displayid.c b/drivers/gpu/drm/drm_displayid.c index 32da557b960f..38ea8203df45 100644 --- a/drivers/gpu/drm/drm_displayid.c +++ b/drivers/gpu/drm/drm_displayid.c @@ -33,11 +33,11 @@ static int validate_displayid(const u8 *displayid, int length, int idx) return 0; } -static const u8 *drm_find_displayid_extension(const struct edid *edid, +static const u8 *drm_find_displayid_extension(const struct drm_edid *drm_edid, int *length, int *idx, int *ext_index) { - const u8 *displayid = drm_find_edid_extension(edid, DISPLAYID_EXT, ext_index); + const u8 *displayid = drm_find_edid_extension(drm_edid, DISPLAYID_EXT, ext_index); const struct displayid_header *base; int ret; @@ -58,12 +58,12 @@ static const u8 *drm_find_displayid_extension(const struct edid *edid, return displayid; } -void displayid_iter_edid_begin(const struct edid *edid, +void displayid_iter_edid_begin(const struct drm_edid *drm_edid, struct displayid_iter *iter) { memset(iter, 0, sizeof(*iter)); - iter->edid = edid; + iter->drm_edid = drm_edid; } static const struct displayid_block * @@ -88,7 +88,7 @@ __displayid_iter_next(struct displayid_iter *iter) { const struct displayid_block *block; - if (!iter->edid) + if (!iter->drm_edid) return NULL; if (iter->section) { @@ -96,7 +96,7 @@ __displayid_iter_next(struct displayid_iter *iter) block = displayid_iter_block(iter); if (WARN_ON(!block)) { iter->section = NULL; - iter->edid = NULL; + iter->drm_edid = NULL; return NULL; } @@ -109,12 +109,12 @@ __displayid_iter_next(struct displayid_iter *iter) } for (;;) { - iter->section = drm_find_displayid_extension(iter->edid, + iter->section = drm_find_displayid_extension(iter->drm_edid, &iter->length, &iter->idx, &iter->ext_index); if (!iter->section) { - iter->edid = NULL; + iter->drm_edid = NULL; return NULL; } diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index bc43e1b32092..929fc0e46751 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -45,10 +45,6 @@ #include "drm_crtc_internal.h" -#define version_greater(edid, maj, min) \ - (((edid)->version > (maj)) || \ - ((edid)->version == (maj) && (edid)->revision > (min))) - static int oui(u8 first, u8 second, u8 third) { return (first << 16) | (second << 8) | third; @@ -96,7 +92,7 @@ static int oui(u8 first, u8 second, u8 third) struct detailed_mode_closure { struct drm_connector *connector; - const struct edid *edid; + const struct drm_edid *drm_edid; bool preferred; u32 quirks; int modes; @@ -1567,6 +1563,24 @@ static const struct drm_display_mode edid_4k_modes[] = { /*** DDC fetch and block validation ***/ +/* + * The opaque EDID type, internal to drm_edid.c. + */ +struct drm_edid { + /* Size allocated for edid */ + size_t size; + const struct edid *edid; +}; + +static bool version_greater(const struct drm_edid *drm_edid, + u8 version, u8 revision) +{ + const struct edid *edid = drm_edid->edid; + + return edid->version > version || + (edid->version == version && edid->revision > revision); +} + static int edid_extension_block_count(const struct edid *edid) { return edid->extensions; @@ -1599,6 +1613,72 @@ static const void *edid_extension_block_data(const struct edid *edid, int index) return edid_block_data(edid, index + 1); } +/* + * Initializer helper for legacy interfaces, where we have no choice but to + * trust edid size. Not for general purpose use. + */ +static const struct drm_edid *drm_edid_legacy_init(struct drm_edid *drm_edid, + const struct edid *edid) +{ + if (!edid) + return NULL; + + memset(drm_edid, 0, sizeof(*drm_edid)); + + drm_edid->edid = edid; + drm_edid->size = edid_size(edid); + + return drm_edid; +} + +/* + * EDID base and extension block iterator. + * + * struct drm_edid_iter iter; + * const u8 *block; + * + * drm_edid_iter_begin(drm_edid, &iter); + * drm_edid_iter_for_each(block, &iter) { + * // do stuff with block + * } + * drm_edid_iter_end(&iter); + */ +struct drm_edid_iter { + const struct drm_edid *drm_edid; + + /* Current block index. */ + int index; +}; + +static void drm_edid_iter_begin(const struct drm_edid *drm_edid, + struct drm_edid_iter *iter) +{ + memset(iter, 0, sizeof(*iter)); + + iter->drm_edid = drm_edid; +} + +static const void *__drm_edid_iter_next(struct drm_edid_iter *iter) +{ + const void *block = NULL; + + if (!iter->drm_edid) + return NULL; + + if (iter->index < edid_block_count(iter->drm_edid->edid)) + block = edid_block_data(iter->drm_edid->edid, iter->index++); + + return block; +} + +#define drm_edid_iter_for_each(__block, __iter) \ + while (((__block) = __drm_edid_iter_next(__iter))) + +static void drm_edid_iter_end(struct drm_edid_iter *iter) +{ + memset(iter, 0, sizeof(*iter)); +} + static const u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; @@ -2362,13 +2442,13 @@ EXPORT_SYMBOL(drm_edid_duplicate); /** * edid_get_quirks - return quirk flags for a given EDID - * @edid: EDID to process + * @drm_edid: EDID to process * * This tells subsequent routines what fixes they need to apply. */ -static u32 edid_get_quirks(const struct edid *edid) +static u32 edid_get_quirks(const struct drm_edid *drm_edid) { - u32 panel_id = edid_extract_panel_id(edid); + u32 panel_id = edid_extract_panel_id(drm_edid->edid); const struct edid_quirk *quirk; int i; @@ -2523,20 +2603,21 @@ vtb_for_each_detailed_block(const u8 *ext, detailed_cb *cb, void *closure) cb((const struct detailed_timing *)(det_base + 18 * i), closure); } -static void -drm_for_each_detailed_block(const struct edid *edid, detailed_cb *cb, void *closure) +static void drm_for_each_detailed_block(const struct drm_edid *drm_edid, + detailed_cb *cb, void *closure) { + struct drm_edid_iter edid_iter; + const u8 *ext; int i; - if (edid == NULL) + if (!drm_edid) return; for (i = 0; i < EDID_DETAILED_TIMINGS; i++) - cb(&(edid->detailed_timings[i]), closure); - - for (i = 0; i < edid_extension_block_count(edid); i++) { - const u8 *ext = edid_extension_block_data(edid, i); + cb(&drm_edid->edid->detailed_timings[i], closure); + drm_edid_iter_begin(drm_edid, &edid_iter); + drm_edid_iter_for_each(ext, &edid_iter) { switch (*ext) { case CEA_EXT: cea_for_each_detailed_block(ext, cb, closure); @@ -2548,6 +2629,7 @@ drm_for_each_detailed_block(const struct edid *edid, detailed_cb *cb, void *clos break; } } + drm_edid_iter_end(&edid_iter); } static void @@ -2568,16 +2650,16 @@ is_rb(const struct detailed_timing *descriptor, void *data) /* EDID 1.4 defines this explicitly. For EDID 1.3, we guess, badly. */ static bool -drm_monitor_supports_rb(const struct edid *edid) +drm_monitor_supports_rb(const struct drm_edid *drm_edid) { - if (edid->revision >= 4) { + if (drm_edid->edid->revision >= 4) { bool ret = false; - drm_for_each_detailed_block(edid, is_rb, &ret); + drm_for_each_detailed_block(drm_edid, is_rb, &ret); return ret; } - return ((edid->input & DRM_EDID_INPUT_DIGITAL) != 0); + return ((drm_edid->edid->input & DRM_EDID_INPUT_DIGITAL) != 0); } static void @@ -2596,11 +2678,11 @@ find_gtf2(const struct detailed_timing *descriptor, void *data) /* Secondary GTF curve kicks in above some break frequency */ static int -drm_gtf2_hbreak(const struct edid *edid) +drm_gtf2_hbreak(const struct drm_edid *drm_edid) { const struct detailed_timing *descriptor = NULL; - drm_for_each_detailed_block(edid, find_gtf2, &descriptor); + drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor); BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.hfreq_start_khz) != 12); @@ -2608,11 +2690,11 @@ drm_gtf2_hbreak(const struct edid *edid) } static int -drm_gtf2_2c(const struct edid *edid) +drm_gtf2_2c(const struct drm_edid *drm_edid) { const struct detailed_timing *descriptor = NULL; - drm_for_each_detailed_block(edid, find_gtf2, &descriptor); + drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor); BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.c) != 13); @@ -2620,11 +2702,11 @@ drm_gtf2_2c(const struct edid *edid) } static int -drm_gtf2_m(const struct edid *edid) +drm_gtf2_m(const struct drm_edid *drm_edid) { const struct detailed_timing *descriptor = NULL; - drm_for_each_detailed_block(edid, find_gtf2, &descriptor); + drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor); BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.m) != 14); @@ -2632,11 +2714,11 @@ drm_gtf2_m(const struct edid *edid) } static int -drm_gtf2_k(const struct edid *edid) +drm_gtf2_k(const struct drm_edid *drm_edid) { const struct detailed_timing *descriptor = NULL; - drm_for_each_detailed_block(edid, find_gtf2, &descriptor); + drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor); BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.k) != 16); @@ -2644,11 +2726,11 @@ drm_gtf2_k(const struct edid *edid) } static int -drm_gtf2_2j(const struct edid *edid) +drm_gtf2_2j(const struct drm_edid *drm_edid) { const struct detailed_timing *descriptor = NULL; - drm_for_each_detailed_block(edid, find_gtf2, &descriptor); + drm_for_each_detailed_block(drm_edid, find_gtf2, &descriptor); BUILD_BUG_ON(offsetof(typeof(*descriptor), data.other_data.data.range.formula.gtf2.j) != 17); @@ -2656,12 +2738,14 @@ drm_gtf2_2j(const struct edid *edid) } /* Get standard timing level (CVT/GTF/DMT). */ -static int standard_timing_level(const struct edid *edid) +static int standard_timing_level(const struct drm_edid *drm_edid) { + const struct edid *edid = drm_edid->edid; + if (edid->revision >= 2) { if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)) return LEVEL_CVT; - if (drm_gtf2_hbreak(edid)) + if (drm_gtf2_hbreak(drm_edid)) return LEVEL_GTF2; if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) return LEVEL_GTF; @@ -2693,9 +2777,9 @@ static int drm_mode_hsync(const struct drm_display_mode *mode) * Take the standard timing params (in this case width, aspect, and refresh) * and convert them into a real mode using CVT/GTF/DMT. */ -static struct drm_display_mode * -drm_mode_std(struct drm_connector *connector, const struct edid *edid, - const struct std_timing *t) +static struct drm_display_mode *drm_mode_std(struct drm_connector *connector, + const struct drm_edid *drm_edid, + const struct std_timing *t) { struct drm_device *dev = connector->dev; struct drm_display_mode *m, *mode = NULL; @@ -2705,7 +2789,7 @@ drm_mode_std(struct drm_connector *connector, const struct edid *edid, >> EDID_TIMING_ASPECT_SHIFT; unsigned vfreq = (t->vfreq_aspect & EDID_TIMING_VFREQ_MASK) >> EDID_TIMING_VFREQ_SHIFT; - int timing_level = standard_timing_level(edid); + int timing_level = standard_timing_level(drm_edid); if (bad_std_timing(t->hsize, t->vfreq_aspect)) return NULL; @@ -2716,7 +2800,7 @@ drm_mode_std(struct drm_connector *connector, const struct edid *edid, vrefresh_rate = vfreq + 60; /* the vdisplay is calculated based on the aspect ratio */ if (aspect_ratio == 0) { - if (edid->revision < 3) + if (drm_edid->edid->revision < 3) vsize = hsize; else vsize = (hsize * 10) / 16; @@ -2759,7 +2843,7 @@ drm_mode_std(struct drm_connector *connector, const struct edid *edid, } /* check whether it can be found in default mode table */ - if (drm_monitor_supports_rb(edid)) { + if (drm_monitor_supports_rb(drm_edid)) { mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate, true); if (mode) @@ -2785,14 +2869,14 @@ drm_mode_std(struct drm_connector *connector, const struct edid *edid, mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); if (!mode) return NULL; - if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) { + if (drm_mode_hsync(mode) > drm_gtf2_hbreak(drm_edid)) { drm_mode_destroy(dev, mode); mode = drm_gtf_mode_complex(dev, hsize, vsize, vrefresh_rate, 0, 0, - drm_gtf2_m(edid), - drm_gtf2_2c(edid), - drm_gtf2_k(edid), - drm_gtf2_2j(edid)); + drm_gtf2_m(drm_edid), + drm_gtf2_2c(drm_edid), + drm_gtf2_k(drm_edid), + drm_gtf2_2j(drm_edid)); } break; case LEVEL_CVT: @@ -2851,7 +2935,7 @@ drm_mode_do_interlace_quirk(struct drm_display_mode *mode, * drm_display_mode. */ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, - const struct edid *edid, + const struct drm_edid *drm_edid, const struct detailed_timing *timing, u32 quirks) { @@ -2939,8 +3023,8 @@ set_size: } if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) { - mode->width_mm = edid->width_cm * 10; - mode->height_mm = edid->height_cm * 10; + mode->width_mm = drm_edid->edid->width_cm * 10; + mode->height_mm = drm_edid->edid->height_cm * 10; } mode->type = DRM_MODE_TYPE_DRIVER; @@ -2998,10 +3082,11 @@ range_pixel_clock(const struct edid *edid, const u8 *t) return t[9] * 10000 + 5001; } -static bool -mode_in_range(const struct drm_display_mode *mode, const struct edid *edid, - const struct detailed_timing *timing) +static bool mode_in_range(const struct drm_display_mode *mode, + const struct drm_edid *drm_edid, + const struct detailed_timing *timing) { + const struct edid *edid = drm_edid->edid; u32 max_clock; const u8 *t = (const u8 *)timing; @@ -3020,7 +3105,7 @@ mode_in_range(const struct drm_display_mode *mode, const struct edid *edid, if (t[13] && mode->hdisplay > 8 * (t[13] + (256 * (t[12]&0x3)))) return false; - if (mode_is_rb(mode) && !drm_monitor_supports_rb(edid)) + if (mode_is_rb(mode) && !drm_monitor_supports_rb(drm_edid)) return false; return true; @@ -3044,16 +3129,16 @@ static bool valid_inferred_mode(const struct drm_connector *connector, return ok; } -static int -drm_dmt_modes_for_range(struct drm_connector *connector, const struct edid *edid, - const struct detailed_timing *timing) +static int drm_dmt_modes_for_range(struct drm_connector *connector, + const struct drm_edid *drm_edid, + const struct detailed_timing *timing) { int i, modes = 0; struct drm_display_mode *newmode; struct drm_device *dev = connector->dev; for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) { - if (mode_in_range(drm_dmt_modes + i, edid, timing) && + if (mode_in_range(drm_dmt_modes + i, drm_edid, timing) && valid_inferred_mode(connector, drm_dmt_modes + i)) { newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]); if (newmode) { @@ -3079,9 +3164,9 @@ void drm_mode_fixup_1366x768(struct drm_display_mode *mode) } } -static int -drm_gtf_modes_for_range(struct drm_connector *connector, const struct edid *edid, - const struct detailed_timing *timing) +static int drm_gtf_modes_for_range(struct drm_connector *connector, + const struct drm_edid *drm_edid, + const struct detailed_timing *timing) { int i, modes = 0; struct drm_display_mode *newmode; @@ -3095,7 +3180,7 @@ drm_gtf_modes_for_range(struct drm_connector *connector, const struct edid *edid return modes; drm_mode_fixup_1366x768(newmode); - if (!mode_in_range(newmode, edid, timing) || + if (!mode_in_range(newmode, drm_edid, timing) || !valid_inferred_mode(connector, newmode)) { drm_mode_destroy(dev, newmode); continue; @@ -3108,14 +3193,14 @@ drm_gtf_modes_for_range(struct drm_connector *connector, const struct edid *edid return modes; } -static int -drm_cvt_modes_for_range(struct drm_connector *connector, const struct edid *edid, - const struct detailed_timing *timing) +static int drm_cvt_modes_for_range(struct drm_connector *connector, + const struct drm_edid *drm_edid, + const struct detailed_timing *timing) { int i, modes = 0; struct drm_display_mode *newmode; struct drm_device *dev = connector->dev; - bool rb = drm_monitor_supports_rb(edid); + bool rb = drm_monitor_supports_rb(drm_edid); for (i = 0; i < ARRAY_SIZE(extra_modes); i++) { const struct minimode *m = &extra_modes[i]; @@ -3125,7 +3210,7 @@ drm_cvt_modes_for_range(struct drm_connector *connector, const struct edid *edid return modes; drm_mode_fixup_1366x768(newmode); - if (!mode_in_range(newmode, edid, timing) || + if (!mode_in_range(newmode, drm_edid, timing) || !valid_inferred_mode(connector, newmode)) { drm_mode_destroy(dev, newmode); continue; @@ -3149,25 +3234,25 @@ do_inferred_modes(const struct detailed_timing *timing, void *c) return; closure->modes += drm_dmt_modes_for_range(closure->connector, - closure->edid, + closure->drm_edid, timing); - if (!version_greater(closure->edid, 1, 1)) + if (!version_greater(closure->drm_edid, 1, 1)) return; /* GTF not defined yet */ switch (range->flags) { case 0x02: /* secondary gtf, XXX could do more */ case 0x00: /* default gtf */ closure->modes += drm_gtf_modes_for_range(closure->connector, - closure->edid, + closure->drm_edid, timing); break; case 0x04: /* cvt, only in 1.4+ */ - if (!version_greater(closure->edid, 1, 3)) + if (!version_greater(closure->drm_edid, 1, 3)) break; closure->modes += drm_cvt_modes_for_range(closure->connector, - closure->edid, + closure->drm_edid, timing); break; case 0x01: /* just the ranges, no formula */ @@ -3176,16 +3261,16 @@ do_inferred_modes(const struct detailed_timing *timing, void *c) } } -static int -add_inferred_modes(struct drm_connector *connector, const struct edid *edid) +static int add_inferred_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid) { struct detailed_mode_closure closure = { .connector = connector, - .edid = edid, + .drm_edid = drm_edid, }; - if (version_greater(edid, 1, 0)) - drm_for_each_detailed_block(edid, do_inferred_modes, &closure); + if (version_greater(drm_edid, 1, 0)) + drm_for_each_detailed_block(drm_edid, do_inferred_modes, &closure); return closure.modes; } @@ -3235,17 +3320,18 @@ do_established_modes(const struct detailed_timing *timing, void *c) * bitmap of the supported "established modes" list (defined above). Tease them * out and add them to the global modes list. */ -static int -add_established_modes(struct drm_connector *connector, const struct edid *edid) +static int add_established_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid) { struct drm_device *dev = connector->dev; + const struct edid *edid = drm_edid->edid; unsigned long est_bits = edid->established_timings.t1 | (edid->established_timings.t2 << 8) | ((edid->established_timings.mfg_rsvd & 0x80) << 9); int i, modes = 0; struct detailed_mode_closure closure = { .connector = connector, - .edid = edid, + .drm_edid = drm_edid, }; for (i = 0; i <= EDID_EST_TIMINGS; i++) { @@ -3260,8 +3346,8 @@ add_established_modes(struct drm_connector *connector, const struct edid *edid) } } - if (version_greater(edid, 1, 0)) - drm_for_each_detailed_block(edid, do_established_modes, + if (version_greater(drm_edid, 1, 0)) + drm_for_each_detailed_block(drm_edid, do_established_modes, &closure); return modes + closure.modes; @@ -3273,7 +3359,6 @@ do_standard_modes(const struct detailed_timing *timing, void *c) struct detailed_mode_closure *closure = c; const struct detailed_non_pixel *data = &timing->data.other_data; struct drm_connector *connector = closure->connector; - const struct edid *edid = closure->edid; int i; if (!is_display_descriptor(timing, EDID_DETAIL_STD_MODES)) @@ -3283,7 +3368,7 @@ do_standard_modes(const struct detailed_timing *timing, void *c) const struct std_timing *std = &data->data.timings[i]; struct drm_display_mode *newmode; - newmode = drm_mode_std(connector, edid, std); + newmode = drm_mode_std(connector, closure->drm_edid, std); if (newmode) { drm_mode_probed_add(connector, newmode); closure->modes++; @@ -3296,28 +3381,28 @@ do_standard_modes(const struct detailed_timing *timing, void *c) * using the appropriate standard (DMT, GTF, or CVT). Grab them from EDID and * add them to the list. */ -static int -add_standard_modes(struct drm_connector *connector, const struct edid *edid) +static int add_standard_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid) { int i, modes = 0; struct detailed_mode_closure closure = { .connector = connector, - .edid = edid, + .drm_edid = drm_edid, }; for (i = 0; i < EDID_STD_TIMINGS; i++) { struct drm_display_mode *newmode; - newmode = drm_mode_std(connector, edid, - &edid->standard_timings[i]); + newmode = drm_mode_std(connector, drm_edid, + &drm_edid->edid->standard_timings[i]); if (newmode) { drm_mode_probed_add(connector, newmode); modes++; } } - if (version_greater(edid, 1, 0)) - drm_for_each_detailed_block(edid, do_standard_modes, + if (version_greater(drm_edid, 1, 0)) + drm_for_each_detailed_block(drm_edid, do_standard_modes, &closure); /* XXX should also look for standard codes in VTB blocks */ @@ -3389,15 +3474,15 @@ do_cvt_mode(const struct detailed_timing *timing, void *c) } static int -add_cvt_modes(struct drm_connector *connector, const struct edid *edid) +add_cvt_modes(struct drm_connector *connector, const struct drm_edid *drm_edid) { struct detailed_mode_closure closure = { .connector = connector, - .edid = edid, + .drm_edid = drm_edid, }; - if (version_greater(edid, 1, 2)) - drm_for_each_detailed_block(edid, do_cvt_mode, &closure); + if (version_greater(drm_edid, 1, 2)) + drm_for_each_detailed_block(drm_edid, do_cvt_mode, &closure); /* XXX should also look for CVT codes in VTB blocks */ @@ -3416,7 +3501,7 @@ do_detailed_mode(const struct detailed_timing *timing, void *c) return; newmode = drm_mode_detailed(closure->connector->dev, - closure->edid, timing, + closure->drm_edid, timing, closure->quirks); if (!newmode) return; @@ -3439,38 +3524,43 @@ do_detailed_mode(const struct detailed_timing *timing, void *c) /* * add_detailed_modes - Add modes from detailed timings * @connector: attached connector - * @edid: EDID block to scan + * @drm_edid: EDID block to scan * @quirks: quirks to apply */ -static int -add_detailed_modes(struct drm_connector *connector, const struct edid *edid, - u32 quirks) +static int add_detailed_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid, u32 quirks) { struct detailed_mode_closure closure = { .connector = connector, - .edid = edid, + .drm_edid = drm_edid, .preferred = true, .quirks = quirks, }; - if (closure.preferred && !version_greater(edid, 1, 3)) + if (closure.preferred && !version_greater(drm_edid, 1, 3)) closure.preferred = - (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING); + (drm_edid->edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING); - drm_for_each_detailed_block(edid, do_detailed_mode, &closure); + drm_for_each_detailed_block(drm_edid, do_detailed_mode, &closure); return closure.modes; } -#define AUDIO_BLOCK 0x01 -#define VIDEO_BLOCK 0x02 -#define VENDOR_BLOCK 0x03 -#define SPEAKER_BLOCK 0x04 -#define HDR_STATIC_METADATA_BLOCK 0x6 -#define USE_EXTENDED_TAG 0x07 -#define EXT_VIDEO_CAPABILITY_BLOCK 0x00 -#define EXT_VIDEO_DATA_BLOCK_420 0x0E -#define EXT_VIDEO_CAP_BLOCK_Y420CMDB 0x0F +/* CTA-861-H Table 60 - CTA Tag Codes */ +#define CTA_DB_AUDIO 1 +#define CTA_DB_VIDEO 2 +#define CTA_DB_VENDOR 3 +#define CTA_DB_SPEAKER 4 +#define CTA_DB_EXTENDED_TAG 7 + +/* CTA-861-H Table 62 - CTA Extended Tag Codes */ +#define CTA_EXT_DB_VIDEO_CAP 0 +#define CTA_EXT_DB_VENDOR 1 +#define CTA_EXT_DB_HDR_STATIC_METADATA 6 +#define CTA_EXT_DB_420_VIDEO_DATA 14 +#define CTA_EXT_DB_420_VIDEO_CAP_MAP 15 +#define CTA_EXT_DB_HF_SCDB 0x79 + #define EDID_BASIC_AUDIO (1 << 6) #define EDID_CEA_YCRCB444 (1 << 5) #define EDID_CEA_YCRCB422 (1 << 4) @@ -3478,10 +3568,13 @@ add_detailed_modes(struct drm_connector *connector, const struct edid *edid, /* * Search EDID for CEA extension block. + * + * FIXME: Prefer not returning pointers to raw EDID data. */ -const u8 *drm_find_edid_extension(const struct edid *edid, +const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid, int ext_id, int *ext_index) { + const struct edid *edid = drm_edid ? drm_edid->edid : NULL; const u8 *edid_ext = NULL; int i; @@ -3504,30 +3597,29 @@ const u8 *drm_find_edid_extension(const struct edid *edid, return edid_ext; } -static const u8 *drm_find_cea_extension(const struct edid *edid) +/* Return true if the EDID has a CTA extension or a DisplayID CTA data block */ +static bool drm_edid_has_cta_extension(const struct drm_edid *drm_edid) { const struct displayid_block *block; struct displayid_iter iter; - const u8 *cea; int ext_index = 0; + bool found = false; /* Look for a top level CEA extension block */ - /* FIXME: make callers iterate through multiple CEA ext blocks? */ - cea = drm_find_edid_extension(edid, CEA_EXT, &ext_index); - if (cea) - return cea; + if (drm_find_edid_extension(drm_edid, CEA_EXT, &ext_index)) + return true; /* CEA blocks can also be found embedded in a DisplayID block */ - displayid_iter_edid_begin(edid, &iter); + displayid_iter_edid_begin(drm_edid, &iter); displayid_iter_for_each(block, &iter) { if (block->tag == DATA_BLOCK_CTA) { - cea = (const u8 *)block; + found = true; break; } } displayid_iter_end(&iter); - return cea; + return found; } static __always_inline const struct drm_display_mode *cea_mode_for_vic(u8 vic) @@ -3792,16 +3884,16 @@ static bool drm_valid_hdmi_vic(u8 vic) return vic > 0 && vic < ARRAY_SIZE(edid_4k_modes); } -static int -add_alternate_cea_modes(struct drm_connector *connector, const struct edid *edid) +static int add_alternate_cea_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid) { struct drm_device *dev = connector->dev; struct drm_display_mode *mode, *tmp; LIST_HEAD(list); int modes = 0; - /* Don't add CEA modes if the CEA extension block is missing */ - if (!drm_find_cea_extension(edid)) + /* Don't add CTA modes if the CTA extension block is missing */ + if (!drm_edid_has_cta_extension(drm_edid)) return 0; /* @@ -4283,24 +4375,6 @@ out: } static int -cea_db_payload_len(const u8 *db) -{ - return db[0] & 0x1f; -} - -static int -cea_db_extended_tag(const u8 *db) -{ - return db[1]; -} - -static int -cea_db_tag(const u8 *db) -{ - return db[0] >> 5; -} - -static int cea_revision(const u8 *cea) { /* @@ -4313,125 +4387,255 @@ cea_revision(const u8 *cea) return cea[1]; } -static int -cea_db_offsets(const u8 *cea, int *start, int *end) +/* + * CTA Data Block iterator. + * + * Iterate through all CTA Data Blocks in both EDID CTA Extensions and DisplayID + * CTA Data Blocks. + * + * struct cea_db *db: + * struct cea_db_iter iter; + * + * cea_db_iter_edid_begin(edid, &iter); + * cea_db_iter_for_each(db, &iter) { + * // do stuff with db + * } + * cea_db_iter_end(&iter); + */ +struct cea_db_iter { + struct drm_edid_iter edid_iter; + struct displayid_iter displayid_iter; + + /* Current Data Block Collection. */ + const u8 *collection; + + /* Current Data Block index in current collection. */ + int index; + + /* End index in current collection. */ + int end; +}; + +/* CTA-861-H section 7.4 CTA Data BLock Collection */ +struct cea_db { + u8 tag_length; + u8 data[]; +} __packed; + +static int cea_db_tag(const struct cea_db *db) { - /* DisplayID CTA extension blocks and top-level CEA EDID - * block header definitions differ in the following bytes: - * 1) Byte 2 of the header specifies length differently, - * 2) Byte 3 is only present in the CEA top level block. - * - * The different definitions for byte 2 follow. - * - * DisplayID CTA extension block defines byte 2 as: - * Number of payload bytes - * - * CEA EDID block defines byte 2 as: - * Byte number (decimal) within this block where the 18-byte - * DTDs begin. If no non-DTD data is present in this extension - * block, the value should be set to 04h (the byte after next). - * If set to 00h, there are no DTDs present in this block and - * no non-DTD data. - */ - if (cea[0] == DATA_BLOCK_CTA) { - /* - * for_each_displayid_db() has already verified - * that these stay within expected bounds. - */ - *start = 3; - *end = *start + cea[2]; - } else if (cea[0] == CEA_EXT) { - /* Data block offset in CEA extension block */ - *start = 4; - *end = cea[2]; - if (*end == 0) - *end = 127; - if (*end < 4 || *end > 127) - return -ERANGE; - } else { - return -EOPNOTSUPP; - } + return db->tag_length >> 5; +} - return 0; +static int cea_db_payload_len(const void *_db) +{ + /* FIXME: Transition to passing struct cea_db * everywhere. */ + const struct cea_db *db = _db; + + return db->tag_length & 0x1f; } -static bool cea_db_is_hdmi_vsdb(const u8 *db) +static const void *cea_db_data(const struct cea_db *db) { - if (cea_db_tag(db) != VENDOR_BLOCK) - return false; + return db->data; +} - if (cea_db_payload_len(db) < 5) - return false; +static bool cea_db_is_extended_tag(const struct cea_db *db, int tag) +{ + return cea_db_tag(db) == CTA_DB_EXTENDED_TAG && + cea_db_payload_len(db) >= 1 && + db->data[0] == tag; +} - return oui(db[3], db[2], db[1]) == HDMI_IEEE_OUI; +static bool cea_db_is_vendor(const struct cea_db *db, int vendor_oui) +{ + const u8 *data = cea_db_data(db); + + return cea_db_tag(db) == CTA_DB_VENDOR && + cea_db_payload_len(db) >= 3 && + oui(data[2], data[1], data[0]) == vendor_oui; } -static bool cea_db_is_hdmi_forum_vsdb(const u8 *db) +static void cea_db_iter_edid_begin(const struct drm_edid *drm_edid, + struct cea_db_iter *iter) { - if (cea_db_tag(db) != VENDOR_BLOCK) - return false; + memset(iter, 0, sizeof(*iter)); - if (cea_db_payload_len(db) < 7) - return false; + drm_edid_iter_begin(drm_edid, &iter->edid_iter); + displayid_iter_edid_begin(drm_edid, &iter->displayid_iter); +} + +static const struct cea_db * +__cea_db_iter_current_block(const struct cea_db_iter *iter) +{ + const struct cea_db *db; + + if (!iter->collection) + return NULL; - return oui(db[3], db[2], db[1]) == HDMI_FORUM_IEEE_OUI; + db = (const struct cea_db *)&iter->collection[iter->index]; + + if (iter->index + sizeof(*db) <= iter->end && + iter->index + sizeof(*db) + cea_db_payload_len(db) <= iter->end) + return db; + + return NULL; } -static bool cea_db_is_microsoft_vsdb(const u8 *db) +/* + * References: + * - VESA E-EDID v1.4 + * - CTA-861-H section 7.3.3 CTA Extension Version 3 + */ +static const void *__cea_db_iter_edid_next(struct cea_db_iter *iter) { - if (cea_db_tag(db) != VENDOR_BLOCK) - return false; + const u8 *ext; - if (cea_db_payload_len(db) != 21) - return false; + drm_edid_iter_for_each(ext, &iter->edid_iter) { + /* Only support CTA Extension revision 3+ */ + if (ext[0] != CEA_EXT || cea_revision(ext) < 3) + continue; + + iter->index = 4; + iter->end = ext[2]; + if (iter->end == 0) + iter->end = 127; + if (iter->end < 4 || iter->end > 127) + continue; + + return ext; + } - return oui(db[3], db[2], db[1]) == MICROSOFT_IEEE_OUI; + return NULL; } -static bool cea_db_is_vcdb(const u8 *db) +/* + * References: + * - DisplayID v1.3 Appendix C: CEA Data Block within a DisplayID Data Block + * - DisplayID v2.0 section 4.10 CTA DisplayID Data Block + * + * Note that the above do not specify any connection between DisplayID Data + * Block revision and CTA Extension versions. + */ +static const void *__cea_db_iter_displayid_next(struct cea_db_iter *iter) { - if (cea_db_tag(db) != USE_EXTENDED_TAG) - return false; + const struct displayid_block *block; - if (cea_db_payload_len(db) != 2) - return false; + displayid_iter_for_each(block, &iter->displayid_iter) { + if (block->tag != DATA_BLOCK_CTA) + continue; - if (cea_db_extended_tag(db) != EXT_VIDEO_CAPABILITY_BLOCK) - return false; + /* + * The displayid iterator has already verified the block bounds + * in displayid_iter_block(). + */ + iter->index = sizeof(*block); + iter->end = iter->index + block->num_bytes; - return true; + return block; + } + + return NULL; } -static bool cea_db_is_y420cmdb(const u8 *db) +static const struct cea_db *__cea_db_iter_next(struct cea_db_iter *iter) { - if (cea_db_tag(db) != USE_EXTENDED_TAG) - return false; + const struct cea_db *db; - if (!cea_db_payload_len(db)) - return false; + if (iter->collection) { + /* Current collection should always be valid. */ + db = __cea_db_iter_current_block(iter); + if (WARN_ON(!db)) { + iter->collection = NULL; + return NULL; + } - if (cea_db_extended_tag(db) != EXT_VIDEO_CAP_BLOCK_Y420CMDB) - return false; + /* Next block in CTA Data Block Collection */ + iter->index += sizeof(*db) + cea_db_payload_len(db); - return true; + db = __cea_db_iter_current_block(iter); + if (db) + return db; + } + + for (;;) { + /* + * Find the next CTA Data Block Collection. First iterate all + * the EDID CTA Extensions, then all the DisplayID CTA blocks. + * + * Per DisplayID v1.3 Appendix B: DisplayID as an EDID + * Extension, it's recommended that DisplayID extensions are + * exposed after all of the CTA Extensions. + */ + iter->collection = __cea_db_iter_edid_next(iter); + if (!iter->collection) + iter->collection = __cea_db_iter_displayid_next(iter); + + if (!iter->collection) + return NULL; + + db = __cea_db_iter_current_block(iter); + if (db) + return db; + } } -static bool cea_db_is_y420vdb(const u8 *db) +#define cea_db_iter_for_each(__db, __iter) \ + while (((__db) = __cea_db_iter_next(__iter))) + +static void cea_db_iter_end(struct cea_db_iter *iter) { - if (cea_db_tag(db) != USE_EXTENDED_TAG) - return false; + displayid_iter_end(&iter->displayid_iter); + drm_edid_iter_end(&iter->edid_iter); - if (!cea_db_payload_len(db)) - return false; + memset(iter, 0, sizeof(*iter)); +} - if (cea_db_extended_tag(db) != EXT_VIDEO_DATA_BLOCK_420) - return false; +static bool cea_db_is_hdmi_vsdb(const struct cea_db *db) +{ + return cea_db_is_vendor(db, HDMI_IEEE_OUI) && + cea_db_payload_len(db) >= 5; +} - return true; +static bool cea_db_is_hdmi_forum_vsdb(const struct cea_db *db) +{ + return cea_db_is_vendor(db, HDMI_FORUM_IEEE_OUI) && + cea_db_payload_len(db) >= 7; +} + +static bool cea_db_is_microsoft_vsdb(const struct cea_db *db) +{ + return cea_db_is_vendor(db, MICROSOFT_IEEE_OUI) && + cea_db_payload_len(db) == 21; +} + +static bool cea_db_is_vcdb(const struct cea_db *db) +{ + return cea_db_is_extended_tag(db, CTA_EXT_DB_VIDEO_CAP) && + cea_db_payload_len(db) == 2; +} + +static bool cea_db_is_hdmi_forum_scdb(const struct cea_db *db) +{ + return cea_db_is_extended_tag(db, CTA_EXT_DB_HF_SCDB) && + cea_db_payload_len(db) >= 7; } -#define for_each_cea_db(cea, i, start, end) \ - for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1) +static bool cea_db_is_y420cmdb(const struct cea_db *db) +{ + return cea_db_is_extended_tag(db, CTA_EXT_DB_420_VIDEO_CAP_MAP); +} + +static bool cea_db_is_y420vdb(const struct cea_db *db) +{ + return cea_db_is_extended_tag(db, CTA_EXT_DB_420_VIDEO_DATA); +} + +static bool cea_db_is_hdmi_hdr_metadata_block(const struct cea_db *db) +{ + return cea_db_is_extended_tag(db, CTA_EXT_DB_HDR_STATIC_METADATA) && + cea_db_payload_len(db) >= 3; +} static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector, const u8 *db) @@ -4473,49 +4677,44 @@ static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector, hdmi->y420_cmdb_map = map; } -static int -add_cea_modes(struct drm_connector *connector, const struct edid *edid) +static int add_cea_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid) { - const u8 *cea = drm_find_cea_extension(edid); - const u8 *db, *hdmi = NULL, *video = NULL; - u8 dbl, hdmi_len, video_len = 0; + const struct cea_db *db; + struct cea_db_iter iter; int modes = 0; - if (cea && cea_revision(cea) >= 3) { - int i, start, end; - - if (cea_db_offsets(cea, &start, &end)) - return 0; - - for_each_cea_db(cea, i, start, end) { - db = &cea[i]; - dbl = cea_db_payload_len(db); - - if (cea_db_tag(db) == VIDEO_BLOCK) { - video = db + 1; - video_len = dbl; - modes += do_cea_modes(connector, video, dbl); - } else if (cea_db_is_hdmi_vsdb(db)) { - hdmi = db; - hdmi_len = dbl; - } else if (cea_db_is_y420vdb(db)) { - const u8 *vdb420 = &db[2]; - - /* Add 4:2:0(only) modes present in EDID */ - modes += do_y420vdb_modes(connector, - vdb420, - dbl - 1); - } + cea_db_iter_edid_begin(drm_edid, &iter); + cea_db_iter_for_each(db, &iter) { + const u8 *hdmi = NULL, *video = NULL; + u8 hdmi_len = 0, video_len = 0; + + if (cea_db_tag(db) == CTA_DB_VIDEO) { + video = cea_db_data(db); + video_len = cea_db_payload_len(db); + modes += do_cea_modes(connector, video, video_len); + } else if (cea_db_is_hdmi_vsdb(db)) { + /* FIXME: Switch to use cea_db_data() */ + hdmi = (const u8 *)db; + hdmi_len = cea_db_payload_len(db); + } else if (cea_db_is_y420vdb(db)) { + const u8 *vdb420 = cea_db_data(db) + 1; + + /* Add 4:2:0(only) modes present in EDID */ + modes += do_y420vdb_modes(connector, vdb420, + cea_db_payload_len(db) - 1); } - } - /* - * We parse the HDMI VSDB after having added the cea modes as we will - * be patching their flags when the sink supports stereo 3D. - */ - if (hdmi) - modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, video, - video_len); + /* + * We parse the HDMI VSDB after having added the cea modes as we + * will be patching their flags when the sink supports stereo + * 3D. + */ + if (hdmi) + modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, + video, video_len); + } + cea_db_iter_end(&iter); return modes; } @@ -4563,20 +4762,6 @@ static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode) mode->clock = clock; } -static bool cea_db_is_hdmi_hdr_metadata_block(const u8 *db) -{ - if (cea_db_tag(db) != USE_EXTENDED_TAG) - return false; - - if (db[1] != HDR_STATIC_METADATA_BLOCK) - return false; - - if (cea_db_payload_len(db) < 3) - return false; - - return true; -} - static uint8_t eotf_supported(const u8 *edid_ext) { return edid_ext[2] & @@ -4654,15 +4839,15 @@ monitor_name(const struct detailed_timing *timing, void *data) *res = timing->data.other_data.data.str.str; } -static int get_monitor_name(const struct edid *edid, char name[13]) +static int get_monitor_name(const struct drm_edid *drm_edid, char name[13]) { const char *edid_name = NULL; int mnl; - if (!edid || !name) + if (!drm_edid || !name) return 0; - drm_for_each_detailed_block(edid, monitor_name, &edid_name); + drm_for_each_detailed_block(drm_edid, monitor_name, &edid_name); for (mnl = 0; edid_name && mnl < 13; mnl++) { if (edid_name[mnl] == 0x0a) break; @@ -4682,14 +4867,22 @@ static int get_monitor_name(const struct edid *edid, char name[13]) */ void drm_edid_get_monitor_name(const struct edid *edid, char *name, int bufsize) { - int name_length; - char buf[13]; + int name_length = 0; if (bufsize <= 0) return; - name_length = min(get_monitor_name(edid, buf), bufsize - 1); - memcpy(name, buf, name_length); + if (edid) { + char buf[13]; + struct drm_edid drm_edid = { + .edid = edid, + .size = edid_size(edid), + }; + + name_length = min(get_monitor_name(&drm_edid, buf), bufsize - 1); + memcpy(name, buf, name_length); + } + name[name_length] = '\0'; } EXPORT_SYMBOL(drm_edid_get_monitor_name); @@ -4709,82 +4902,70 @@ static void clear_eld(struct drm_connector *connector) /* * drm_edid_to_eld - build ELD from EDID * @connector: connector corresponding to the HDMI/DP sink - * @edid: EDID to parse + * @drm_edid: EDID to parse * * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The * HDCP and Port_ID ELD fields are left for the graphics driver to fill in. */ static void drm_edid_to_eld(struct drm_connector *connector, - const struct edid *edid) + const struct drm_edid *drm_edid) { + const struct drm_display_info *info = &connector->display_info; + const struct cea_db *db; + struct cea_db_iter iter; uint8_t *eld = connector->eld; - const u8 *cea; - const u8 *db; int total_sad_count = 0; int mnl; - int dbl; clear_eld(connector); - if (!edid) + if (!drm_edid) return; - cea = drm_find_cea_extension(edid); - if (!cea) { - DRM_DEBUG_KMS("ELD: no CEA Extension found\n"); - return; - } - - mnl = get_monitor_name(edid, &eld[DRM_ELD_MONITOR_NAME_STRING]); + mnl = get_monitor_name(drm_edid, &eld[DRM_ELD_MONITOR_NAME_STRING]); DRM_DEBUG_KMS("ELD monitor %s\n", &eld[DRM_ELD_MONITOR_NAME_STRING]); - eld[DRM_ELD_CEA_EDID_VER_MNL] = cea[1] << DRM_ELD_CEA_EDID_VER_SHIFT; + eld[DRM_ELD_CEA_EDID_VER_MNL] = info->cea_rev << DRM_ELD_CEA_EDID_VER_SHIFT; eld[DRM_ELD_CEA_EDID_VER_MNL] |= mnl; eld[DRM_ELD_VER] = DRM_ELD_VER_CEA861D; - eld[DRM_ELD_MANUFACTURER_NAME0] = edid->mfg_id[0]; - eld[DRM_ELD_MANUFACTURER_NAME1] = edid->mfg_id[1]; - eld[DRM_ELD_PRODUCT_CODE0] = edid->prod_code[0]; - eld[DRM_ELD_PRODUCT_CODE1] = edid->prod_code[1]; + eld[DRM_ELD_MANUFACTURER_NAME0] = drm_edid->edid->mfg_id[0]; + eld[DRM_ELD_MANUFACTURER_NAME1] = drm_edid->edid->mfg_id[1]; + eld[DRM_ELD_PRODUCT_CODE0] = drm_edid->edid->prod_code[0]; + eld[DRM_ELD_PRODUCT_CODE1] = drm_edid->edid->prod_code[1]; - if (cea_revision(cea) >= 3) { - int i, start, end; + cea_db_iter_edid_begin(drm_edid, &iter); + cea_db_iter_for_each(db, &iter) { + const u8 *data = cea_db_data(db); + int len = cea_db_payload_len(db); int sad_count; - if (cea_db_offsets(cea, &start, &end)) { - start = 0; - end = 0; - } - - for_each_cea_db(cea, i, start, end) { - db = &cea[i]; - dbl = cea_db_payload_len(db); - - switch (cea_db_tag(db)) { - case AUDIO_BLOCK: - /* Audio Data Block, contains SADs */ - sad_count = min(dbl / 3, 15 - total_sad_count); - if (sad_count >= 1) - memcpy(&eld[DRM_ELD_CEA_SAD(mnl, total_sad_count)], - &db[1], sad_count * 3); - total_sad_count += sad_count; - break; - case SPEAKER_BLOCK: - /* Speaker Allocation Data Block */ - if (dbl >= 1) - eld[DRM_ELD_SPEAKER] = db[1]; - break; - case VENDOR_BLOCK: - /* HDMI Vendor-Specific Data Block */ - if (cea_db_is_hdmi_vsdb(db)) - drm_parse_hdmi_vsdb_audio(connector, db); - break; - default: - break; - } + switch (cea_db_tag(db)) { + case CTA_DB_AUDIO: + /* Audio Data Block, contains SADs */ + sad_count = min(len / 3, 15 - total_sad_count); + if (sad_count >= 1) + memcpy(&eld[DRM_ELD_CEA_SAD(mnl, total_sad_count)], + data, sad_count * 3); + total_sad_count += sad_count; + break; + case CTA_DB_SPEAKER: + /* Speaker Allocation Data Block */ + if (len >= 1) + eld[DRM_ELD_SPEAKER] = data[0]; + break; + case CTA_DB_VENDOR: + /* HDMI Vendor-Specific Data Block */ + if (cea_db_is_hdmi_vsdb(db)) + drm_parse_hdmi_vsdb_audio(connector, (const u8 *)db); + break; + default: + break; } } + cea_db_iter_end(&iter); + eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= total_sad_count << DRM_ELD_SAD_COUNT_SHIFT; if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort || @@ -4800,6 +4981,40 @@ static void drm_edid_to_eld(struct drm_connector *connector, drm_eld_size(eld), total_sad_count); } +static int _drm_edid_to_sad(const struct drm_edid *drm_edid, + struct cea_sad **sads) +{ + const struct cea_db *db; + struct cea_db_iter iter; + int count = 0; + + cea_db_iter_edid_begin(drm_edid, &iter); + cea_db_iter_for_each(db, &iter) { + if (cea_db_tag(db) == CTA_DB_AUDIO) { + int j; + + count = cea_db_payload_len(db) / 3; /* SAD is 3B */ + *sads = kcalloc(count, sizeof(**sads), GFP_KERNEL); + if (!*sads) + return -ENOMEM; + for (j = 0; j < count; j++) { + const u8 *sad = &db->data[j * 3]; + + (*sads)[j].format = (sad[0] & 0x78) >> 3; + (*sads)[j].channels = sad[0] & 0x7; + (*sads)[j].freq = sad[1] & 0x7F; + (*sads)[j].byte2 = sad[2]; + } + break; + } + } + cea_db_iter_end(&iter); + + DRM_DEBUG_KMS("Found %d Short Audio Descriptors\n", count); + + return count; +} + /** * drm_edid_to_sad - extracts SADs from EDID * @edid: EDID to parse @@ -4813,53 +5028,37 @@ static void drm_edid_to_eld(struct drm_connector *connector, */ int drm_edid_to_sad(const struct edid *edid, struct cea_sad **sads) { - int count = 0; - int i, start, end, dbl; - const u8 *cea; - - cea = drm_find_cea_extension(edid); - if (!cea) { - DRM_DEBUG_KMS("SAD: no CEA Extension found\n"); - return 0; - } - - if (cea_revision(cea) < 3) { - DRM_DEBUG_KMS("SAD: wrong CEA revision\n"); - return 0; - } - - if (cea_db_offsets(cea, &start, &end)) { - DRM_DEBUG_KMS("SAD: invalid data block offsets\n"); - return -EPROTO; - } - - for_each_cea_db(cea, i, start, end) { - const u8 *db = &cea[i]; + struct drm_edid drm_edid; - if (cea_db_tag(db) == AUDIO_BLOCK) { - int j; + return _drm_edid_to_sad(drm_edid_legacy_init(&drm_edid, edid), sads); +} +EXPORT_SYMBOL(drm_edid_to_sad); - dbl = cea_db_payload_len(db); +static int _drm_edid_to_speaker_allocation(const struct drm_edid *drm_edid, + u8 **sadb) +{ + const struct cea_db *db; + struct cea_db_iter iter; + int count = 0; - count = dbl / 3; /* SAD is 3B */ - *sads = kcalloc(count, sizeof(**sads), GFP_KERNEL); - if (!*sads) + cea_db_iter_edid_begin(drm_edid, &iter); + cea_db_iter_for_each(db, &iter) { + if (cea_db_tag(db) == CTA_DB_SPEAKER && + cea_db_payload_len(db) == 3) { + *sadb = kmemdup(db->data, cea_db_payload_len(db), + GFP_KERNEL); + if (!*sadb) return -ENOMEM; - for (j = 0; j < count; j++) { - const u8 *sad = &db[1 + j * 3]; - - (*sads)[j].format = (sad[0] & 0x78) >> 3; - (*sads)[j].channels = sad[0] & 0x7; - (*sads)[j].freq = sad[1] & 0x7F; - (*sads)[j].byte2 = sad[2]; - } + count = cea_db_payload_len(db); break; } } + cea_db_iter_end(&iter); + + DRM_DEBUG_KMS("Found %d Speaker Allocation Data Blocks\n", count); return count; } -EXPORT_SYMBOL(drm_edid_to_sad); /** * drm_edid_to_speaker_allocation - extracts Speaker Allocation Data Blocks from EDID @@ -4875,44 +5074,10 @@ EXPORT_SYMBOL(drm_edid_to_sad); */ int drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb) { - int count = 0; - int i, start, end, dbl; - const u8 *cea; - - cea = drm_find_cea_extension(edid); - if (!cea) { - DRM_DEBUG_KMS("SAD: no CEA Extension found\n"); - return 0; - } - - if (cea_revision(cea) < 3) { - DRM_DEBUG_KMS("SAD: wrong CEA revision\n"); - return 0; - } + struct drm_edid drm_edid; - if (cea_db_offsets(cea, &start, &end)) { - DRM_DEBUG_KMS("SAD: invalid data block offsets\n"); - return -EPROTO; - } - - for_each_cea_db(cea, i, start, end) { - const u8 *db = &cea[i]; - - if (cea_db_tag(db) == SPEAKER_BLOCK) { - dbl = cea_db_payload_len(db); - - /* Speaker Allocation Data Block */ - if (dbl == 3) { - *sadb = kmemdup(&db[1], dbl, GFP_KERNEL); - if (!*sadb) - return -ENOMEM; - count = dbl; - break; - } - } - } - - return count; + return _drm_edid_to_speaker_allocation(drm_edid_legacy_init(&drm_edid, edid), + sadb); } EXPORT_SYMBOL(drm_edid_to_speaker_allocation); @@ -4957,6 +5122,28 @@ int drm_av_sync_delay(struct drm_connector *connector, } EXPORT_SYMBOL(drm_av_sync_delay); +static bool _drm_detect_hdmi_monitor(const struct drm_edid *drm_edid) +{ + const struct cea_db *db; + struct cea_db_iter iter; + bool hdmi = false; + + /* + * Because HDMI identifier is in Vendor Specific Block, + * search it from all data blocks of CEA extension. + */ + cea_db_iter_edid_begin(drm_edid, &iter); + cea_db_iter_for_each(db, &iter) { + if (cea_db_is_hdmi_vsdb(db)) { + hdmi = true; + break; + } + } + cea_db_iter_end(&iter); + + return hdmi; +} + /** * drm_detect_hdmi_monitor - detect whether monitor is HDMI * @edid: monitor EDID information @@ -4970,29 +5157,53 @@ EXPORT_SYMBOL(drm_av_sync_delay); */ bool drm_detect_hdmi_monitor(const struct edid *edid) { + struct drm_edid drm_edid; + + return _drm_detect_hdmi_monitor(drm_edid_legacy_init(&drm_edid, edid)); +} +EXPORT_SYMBOL(drm_detect_hdmi_monitor); + +static bool _drm_detect_monitor_audio(const struct drm_edid *drm_edid) +{ + struct drm_edid_iter edid_iter; + const struct cea_db *db; + struct cea_db_iter iter; const u8 *edid_ext; - int i; - int start_offset, end_offset; + bool has_audio = false; - edid_ext = drm_find_cea_extension(edid); - if (!edid_ext) - return false; + drm_edid_iter_begin(drm_edid, &edid_iter); + drm_edid_iter_for_each(edid_ext, &edid_iter) { + if (edid_ext[0] == CEA_EXT) { + has_audio = edid_ext[3] & EDID_BASIC_AUDIO; + if (has_audio) + break; + } + } + drm_edid_iter_end(&edid_iter); - if (cea_db_offsets(edid_ext, &start_offset, &end_offset)) - return false; + if (has_audio) { + DRM_DEBUG_KMS("Monitor has basic audio support\n"); + goto end; + } - /* - * Because HDMI identifier is in Vendor Specific Block, - * search it from all data blocks of CEA extension. - */ - for_each_cea_db(edid_ext, i, start_offset, end_offset) { - if (cea_db_is_hdmi_vsdb(&edid_ext[i])) - return true; + cea_db_iter_edid_begin(drm_edid, &iter); + cea_db_iter_for_each(db, &iter) { + if (cea_db_tag(db) == CTA_DB_AUDIO) { + const u8 *data = cea_db_data(db); + int i; + + for (i = 0; i < cea_db_payload_len(db); i += 3) + DRM_DEBUG_KMS("CEA audio format %d\n", + (data[i] >> 3) & 0xf); + has_audio = true; + break; + } } + cea_db_iter_end(&iter); - return false; +end: + return has_audio; } -EXPORT_SYMBOL(drm_detect_hdmi_monitor); /** * drm_detect_monitor_audio - check monitor audio capability @@ -5008,37 +5219,9 @@ EXPORT_SYMBOL(drm_detect_hdmi_monitor); */ bool drm_detect_monitor_audio(const struct edid *edid) { - const u8 *edid_ext; - int i, j; - bool has_audio = false; - int start_offset, end_offset; - - edid_ext = drm_find_cea_extension(edid); - if (!edid_ext) - goto end; - - has_audio = (edid_ext[0] == CEA_EXT && - (edid_ext[3] & EDID_BASIC_AUDIO) != 0); + struct drm_edid drm_edid; - if (has_audio) { - DRM_DEBUG_KMS("Monitor has basic audio support\n"); - goto end; - } - - if (cea_db_offsets(edid_ext, &start_offset, &end_offset)) - goto end; - - for_each_cea_db(edid_ext, i, start_offset, end_offset) { - if (cea_db_tag(&edid_ext[i]) == AUDIO_BLOCK) { - has_audio = true; - for (j = 1; j < cea_db_payload_len(&edid_ext[i]) + 1; j += 3) - DRM_DEBUG_KMS("CEA audio format %d\n", - (edid_ext[i + j] >> 3) & 0xf); - goto end; - } - } -end: - return has_audio; + return _drm_detect_monitor_audio(drm_edid_legacy_init(&drm_edid, edid)); } EXPORT_SYMBOL(drm_detect_monitor_audio); @@ -5117,17 +5300,18 @@ static void drm_parse_ycbcr420_deep_color_info(struct drm_connector *connector, hdmi->y420_dc_modes = dc_mask; } -static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector, - const u8 *hf_vsdb) +/* Sink Capability Data Structure */ +static void drm_parse_hdmi_forum_scds(struct drm_connector *connector, + const u8 *hf_scds) { struct drm_display_info *display = &connector->display_info; struct drm_hdmi_info *hdmi = &display->hdmi; display->has_hdmi_infoframe = true; - if (hf_vsdb[6] & 0x80) { + if (hf_scds[6] & 0x80) { hdmi->scdc.supported = true; - if (hf_vsdb[6] & 0x40) + if (hf_scds[6] & 0x40) hdmi->scdc.read_request = true; } @@ -5140,9 +5324,9 @@ static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector, * Lets check it out. */ - if (hf_vsdb[5]) { + if (hf_scds[5]) { /* max clock is 5000 KHz times block value */ - u32 max_tmds_clock = hf_vsdb[5] * 5000; + u32 max_tmds_clock = hf_scds[5] * 5000; struct drm_scdc *scdc = &hdmi->scdc; if (max_tmds_clock > 340000) { @@ -5155,42 +5339,42 @@ static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector, scdc->scrambling.supported = true; /* Few sinks support scrambling for clocks < 340M */ - if ((hf_vsdb[6] & 0x8)) + if ((hf_scds[6] & 0x8)) scdc->scrambling.low_rates = true; } } - if (hf_vsdb[7]) { + if (hf_scds[7]) { u8 max_frl_rate; u8 dsc_max_frl_rate; u8 dsc_max_slices; struct drm_hdmi_dsc_cap *hdmi_dsc = &hdmi->dsc_cap; DRM_DEBUG_KMS("hdmi_21 sink detected. parsing edid\n"); - max_frl_rate = (hf_vsdb[7] & DRM_EDID_MAX_FRL_RATE_MASK) >> 4; + max_frl_rate = (hf_scds[7] & DRM_EDID_MAX_FRL_RATE_MASK) >> 4; drm_get_max_frl_rate(max_frl_rate, &hdmi->max_lanes, &hdmi->max_frl_rate_per_lane); - hdmi_dsc->v_1p2 = hf_vsdb[11] & DRM_EDID_DSC_1P2; + hdmi_dsc->v_1p2 = hf_scds[11] & DRM_EDID_DSC_1P2; if (hdmi_dsc->v_1p2) { - hdmi_dsc->native_420 = hf_vsdb[11] & DRM_EDID_DSC_NATIVE_420; - hdmi_dsc->all_bpp = hf_vsdb[11] & DRM_EDID_DSC_ALL_BPP; + hdmi_dsc->native_420 = hf_scds[11] & DRM_EDID_DSC_NATIVE_420; + hdmi_dsc->all_bpp = hf_scds[11] & DRM_EDID_DSC_ALL_BPP; - if (hf_vsdb[11] & DRM_EDID_DSC_16BPC) + if (hf_scds[11] & DRM_EDID_DSC_16BPC) hdmi_dsc->bpc_supported = 16; - else if (hf_vsdb[11] & DRM_EDID_DSC_12BPC) + else if (hf_scds[11] & DRM_EDID_DSC_12BPC) hdmi_dsc->bpc_supported = 12; - else if (hf_vsdb[11] & DRM_EDID_DSC_10BPC) + else if (hf_scds[11] & DRM_EDID_DSC_10BPC) hdmi_dsc->bpc_supported = 10; else hdmi_dsc->bpc_supported = 0; - dsc_max_frl_rate = (hf_vsdb[12] & DRM_EDID_DSC_MAX_FRL_RATE_MASK) >> 4; + dsc_max_frl_rate = (hf_scds[12] & DRM_EDID_DSC_MAX_FRL_RATE_MASK) >> 4; drm_get_max_frl_rate(dsc_max_frl_rate, &hdmi_dsc->max_lanes, &hdmi_dsc->max_frl_rate_per_lane); - hdmi_dsc->total_chunk_kbytes = hf_vsdb[13] & DRM_EDID_DSC_TOTAL_CHUNK_KBYTES; + hdmi_dsc->total_chunk_kbytes = hf_scds[13] & DRM_EDID_DSC_TOTAL_CHUNK_KBYTES; - dsc_max_slices = hf_vsdb[12] & DRM_EDID_DSC_MAX_SLICES; + dsc_max_slices = hf_scds[12] & DRM_EDID_DSC_MAX_SLICES; switch (dsc_max_slices) { case 1: hdmi_dsc->max_slices = 1; @@ -5228,7 +5412,7 @@ static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector, } } - drm_parse_ycbcr420_deep_color_info(connector, hf_vsdb); + drm_parse_ycbcr420_deep_color_info(connector, hf_scds); } static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector, @@ -5332,48 +5516,55 @@ static void drm_parse_microsoft_vsdb(struct drm_connector *connector, } static void drm_parse_cea_ext(struct drm_connector *connector, - const struct edid *edid) + const struct drm_edid *drm_edid) { struct drm_display_info *info = &connector->display_info; + struct drm_edid_iter edid_iter; + const struct cea_db *db; + struct cea_db_iter iter; const u8 *edid_ext; - int i, start, end; - edid_ext = drm_find_cea_extension(edid); - if (!edid_ext) - return; + drm_edid_iter_begin(drm_edid, &edid_iter); + drm_edid_iter_for_each(edid_ext, &edid_iter) { + if (edid_ext[0] != CEA_EXT) + continue; - info->cea_rev = edid_ext[1]; + if (!info->cea_rev) + info->cea_rev = edid_ext[1]; - /* The existence of a CEA block should imply RGB support */ - info->color_formats = DRM_COLOR_FORMAT_RGB444; + if (info->cea_rev != edid_ext[1]) + DRM_DEBUG_KMS("CEA extension version mismatch %u != %u\n", + info->cea_rev, edid_ext[1]); - /* CTA DisplayID Data Block does not have byte #3 */ - if (edid_ext[0] == CEA_EXT) { + /* The existence of a CTA extension should imply RGB support */ + info->color_formats = DRM_COLOR_FORMAT_RGB444; if (edid_ext[3] & EDID_CEA_YCRCB444) info->color_formats |= DRM_COLOR_FORMAT_YCBCR444; if (edid_ext[3] & EDID_CEA_YCRCB422) info->color_formats |= DRM_COLOR_FORMAT_YCBCR422; } + drm_edid_iter_end(&edid_iter); - if (cea_db_offsets(edid_ext, &start, &end)) - return; - - for_each_cea_db(edid_ext, i, start, end) { - const u8 *db = &edid_ext[i]; + cea_db_iter_edid_begin(drm_edid, &iter); + cea_db_iter_for_each(db, &iter) { + /* FIXME: convert parsers to use struct cea_db */ + const u8 *data = (const u8 *)db; if (cea_db_is_hdmi_vsdb(db)) - drm_parse_hdmi_vsdb_video(connector, db); - if (cea_db_is_hdmi_forum_vsdb(db)) - drm_parse_hdmi_forum_vsdb(connector, db); - if (cea_db_is_microsoft_vsdb(db)) - drm_parse_microsoft_vsdb(connector, db); - if (cea_db_is_y420cmdb(db)) - drm_parse_y420cmdb_bitmap(connector, db); - if (cea_db_is_vcdb(db)) - drm_parse_vcdb(connector, db); - if (cea_db_is_hdmi_hdr_metadata_block(db)) - drm_parse_hdr_metadata_block(connector, db); + drm_parse_hdmi_vsdb_video(connector, data); + else if (cea_db_is_hdmi_forum_vsdb(db) || + cea_db_is_hdmi_forum_scdb(db)) + drm_parse_hdmi_forum_scds(connector, data); + else if (cea_db_is_microsoft_vsdb(db)) + drm_parse_microsoft_vsdb(connector, data); + else if (cea_db_is_y420cmdb(db)) + drm_parse_y420cmdb_bitmap(connector, data); + else if (cea_db_is_vcdb(db)) + drm_parse_vcdb(connector, data); + else if (cea_db_is_hdmi_hdr_metadata_block(db)) + drm_parse_hdr_metadata_block(connector, data); } + cea_db_iter_end(&iter); } static @@ -5400,16 +5591,15 @@ void get_monitor_range(const struct detailed_timing *timing, monitor_range->max_vfreq = range->max_vfreq; } -static -void drm_get_monitor_range(struct drm_connector *connector, - const struct edid *edid) +static void drm_get_monitor_range(struct drm_connector *connector, + const struct drm_edid *drm_edid) { struct drm_display_info *info = &connector->display_info; - if (!version_greater(edid, 1, 1)) + if (!version_greater(drm_edid, 1, 1)) return; - drm_for_each_detailed_block(edid, get_monitor_range, + drm_for_each_detailed_block(drm_edid, get_monitor_range, &info->monitor_range); DRM_DEBUG_KMS("Supported Monitor Refresh rate range is %d Hz - %d Hz\n", @@ -5469,12 +5659,13 @@ static void drm_parse_vesa_mso_data(struct drm_connector *connector, info->mso_stream_count, info->mso_pixel_overlap); } -static void drm_update_mso(struct drm_connector *connector, const struct edid *edid) +static void drm_update_mso(struct drm_connector *connector, + const struct drm_edid *drm_edid) { const struct displayid_block *block; struct displayid_iter iter; - displayid_iter_edid_begin(edid, &iter); + displayid_iter_edid_begin(drm_edid, &iter); displayid_iter_for_each(block, &iter) { if (block->tag == DATA_BLOCK_2_VENDOR_SPECIFIC) drm_parse_vesa_mso_data(connector, block); @@ -5513,18 +5704,20 @@ drm_reset_display_info(struct drm_connector *connector) info->mso_pixel_overlap = 0; } -u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid) +static u32 update_display_info(struct drm_connector *connector, + const struct drm_edid *drm_edid) { struct drm_display_info *info = &connector->display_info; + const struct edid *edid = drm_edid->edid; - u32 quirks = edid_get_quirks(edid); + u32 quirks = edid_get_quirks(drm_edid); drm_reset_display_info(connector); info->width_mm = edid->width_cm * 10; info->height_mm = edid->height_cm * 10; - drm_get_monitor_range(connector, edid); + drm_get_monitor_range(connector, drm_edid); if (edid->revision < 3) goto out; @@ -5533,7 +5726,7 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi goto out; info->color_formats |= DRM_COLOR_FORMAT_RGB444; - drm_parse_cea_ext(connector, edid); + drm_parse_cea_ext(connector, drm_edid); /* * Digital sink with "DFP 1.x compliant TMDS" according to EDID 1.3? @@ -5586,7 +5779,7 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422) info->color_formats |= DRM_COLOR_FORMAT_YCBCR422; - drm_update_mso(connector, edid); + drm_update_mso(connector, drm_edid); out: if (quirks & EDID_QUIRK_NON_DESKTOP) { @@ -5598,6 +5791,14 @@ out: return quirks; } +u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid) +{ + struct drm_edid drm_edid; + + return update_display_info(connector, + drm_edid_legacy_init(&drm_edid, edid)); +} + static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev, struct displayid_detailed_timings_1 *timings, bool type_7) @@ -5673,13 +5874,13 @@ static int add_displayid_detailed_1_modes(struct drm_connector *connector, } static int add_displayid_detailed_modes(struct drm_connector *connector, - const struct edid *edid) + const struct drm_edid *drm_edid) { const struct displayid_block *block; struct displayid_iter iter; int num_modes = 0; - displayid_iter_edid_begin(edid, &iter); + displayid_iter_edid_begin(drm_edid, &iter); displayid_iter_for_each(block, &iter) { if (block->tag == DATA_BLOCK_TYPE_1_DETAILED_TIMING || block->tag == DATA_BLOCK_2_TYPE_7_DETAILED_TIMING) @@ -5691,24 +5892,26 @@ static int add_displayid_detailed_modes(struct drm_connector *connector, } static int drm_edid_connector_update(struct drm_connector *connector, - const struct edid *edid) + const struct drm_edid *drm_edid) { int num_modes = 0; u32 quirks; - if (edid == NULL) { + if (!drm_edid) { + drm_reset_display_info(connector); clear_eld(connector); return 0; } - drm_edid_to_eld(connector, edid); - /* * CEA-861-F adds ycbcr capability map block, for HDMI 2.0 sinks. * To avoid multiple parsing of same block, lets parse that map * from sink info, before parsing CEA modes. */ - quirks = drm_add_display_info(connector, edid); + quirks = update_display_info(connector, drm_edid); + + /* Depends on info->cea_rev set by update_display_info() above */ + drm_edid_to_eld(connector, drm_edid); /* * EDID spec says modes should be preferred in this order: @@ -5724,15 +5927,15 @@ static int drm_edid_connector_update(struct drm_connector *connector, * * XXX order for additional mode types in extension blocks? */ - num_modes += add_detailed_modes(connector, edid, quirks); - num_modes += add_cvt_modes(connector, edid); - num_modes += add_standard_modes(connector, edid); - num_modes += add_established_modes(connector, edid); - num_modes += add_cea_modes(connector, edid); - num_modes += add_alternate_cea_modes(connector, edid); - num_modes += add_displayid_detailed_modes(connector, edid); - if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) - num_modes += add_inferred_modes(connector, edid); + num_modes += add_detailed_modes(connector, drm_edid, quirks); + num_modes += add_cvt_modes(connector, drm_edid); + num_modes += add_standard_modes(connector, drm_edid); + num_modes += add_established_modes(connector, drm_edid); + num_modes += add_cea_modes(connector, drm_edid); + num_modes += add_alternate_cea_modes(connector, drm_edid); + num_modes += add_displayid_detailed_modes(connector, drm_edid); + if (drm_edid->edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) + num_modes += add_inferred_modes(connector, drm_edid); if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) edid_fixup_preferred(connector, quirks); @@ -5765,13 +5968,16 @@ static int drm_edid_connector_update(struct drm_connector *connector, */ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) { + struct drm_edid drm_edid; + if (edid && !drm_edid_is_valid(edid)) { drm_warn(connector->dev, "%s: EDID invalid.\n", connector->name); edid = NULL; } - return drm_edid_connector_update(connector, edid); + return drm_edid_connector_update(connector, + drm_edid_legacy_init(&drm_edid, edid)); } EXPORT_SYMBOL(drm_add_edid_modes); @@ -6166,15 +6372,15 @@ static void drm_parse_tiled_block(struct drm_connector *connector, } } -void drm_update_tile_info(struct drm_connector *connector, - const struct edid *edid) +static void _drm_update_tile_info(struct drm_connector *connector, + const struct drm_edid *drm_edid) { const struct displayid_block *block; struct displayid_iter iter; connector->has_tile = false; - displayid_iter_edid_begin(edid, &iter); + displayid_iter_edid_begin(drm_edid, &iter); displayid_iter_for_each(block, &iter) { if (block->tag == DATA_BLOCK_TILED_DISPLAY) drm_parse_tiled_block(connector, block); @@ -6186,3 +6392,11 @@ void drm_update_tile_info(struct drm_connector *connector, connector->tile_group = NULL; } } + +void drm_update_tile_info(struct drm_connector *connector, + const struct edid *edid) +{ + struct drm_edid drm_edid; + + _drm_update_tile_info(connector, drm_edid_legacy_init(&drm_edid, edid)); +} diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c index 72e982323a5e..a940024c8087 100644 --- a/drivers/gpu/drm/drm_encoder.c +++ b/drivers/gpu/drm/drm_encoder.c @@ -226,7 +226,7 @@ void *__drmm_encoder_alloc(struct drm_device *dev, size_t size, size_t offset, container = drmm_kzalloc(dev, size, GFP_KERNEL); if (!container) - return ERR_PTR(-EINVAL); + return ERR_PTR(-ENOMEM); encoder = container + offset; diff --git a/drivers/gpu/drm/drm_gem_atomic_helper.c b/drivers/gpu/drm/drm_gem_atomic_helper.c index a5026f617739..f16d60217c6c 100644 --- a/drivers/gpu/drm/drm_gem_atomic_helper.c +++ b/drivers/gpu/drm/drm_gem_atomic_helper.c @@ -169,8 +169,10 @@ int drm_gem_plane_helper_prepare_fb(struct drm_plane *plane, struct drm_gem_object *obj = drm_gem_fb_get_obj(state->fb, i); struct dma_fence *new; - if (WARN_ON_ONCE(!obj)) - continue; + if (!obj) { + ret = -EINVAL; + goto error; + } ret = dma_resv_get_singleton(obj->resv, usage, &new); if (ret) diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index f4619803acd0..61339a9cd010 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -53,7 +53,11 @@ MODULE_IMPORT_NS(DMA_BUF); struct drm_gem_object *drm_gem_fb_get_obj(struct drm_framebuffer *fb, unsigned int plane) { - if (plane >= ARRAY_SIZE(fb->obj)) + struct drm_device *dev = fb->dev; + + if (drm_WARN_ON_ONCE(dev, plane >= ARRAY_SIZE(fb->obj))) + return NULL; + else if (drm_WARN_ON_ONCE(dev, !fb->obj[plane])) return NULL; return fb->obj[plane]; @@ -92,9 +96,9 @@ drm_gem_fb_init(struct drm_device *dev, */ void drm_gem_fb_destroy(struct drm_framebuffer *fb) { - size_t i; + unsigned int i; - for (i = 0; i < ARRAY_SIZE(fb->obj); i++) + for (i = 0; i < fb->format->num_planes; i++) drm_gem_object_put(fb->obj[i]); drm_framebuffer_cleanup(fb); @@ -329,24 +333,26 @@ EXPORT_SYMBOL_GPL(drm_gem_fb_create_with_dirty); * The argument returns the addresses of the data stored in each BO. This * is different from @map if the framebuffer's offsets field is non-zero. * + * Both, @map and @data, must each refer to arrays with at least + * fb->format->num_planes elements. + * * See drm_gem_fb_vunmap() for unmapping. * * Returns: * 0 on success, or a negative errno code otherwise. */ -int drm_gem_fb_vmap(struct drm_framebuffer *fb, - struct iosys_map map[static DRM_FORMAT_MAX_PLANES], - struct iosys_map data[DRM_FORMAT_MAX_PLANES]) +int drm_gem_fb_vmap(struct drm_framebuffer *fb, struct iosys_map *map, + struct iosys_map *data) { struct drm_gem_object *obj; unsigned int i; int ret; - for (i = 0; i < DRM_FORMAT_MAX_PLANES; ++i) { + for (i = 0; i < fb->format->num_planes; ++i) { obj = drm_gem_fb_get_obj(fb, i); if (!obj) { - iosys_map_clear(&map[i]); - continue; + ret = -EINVAL; + goto err_drm_gem_vunmap; } ret = drm_gem_vmap(obj, &map[i]); if (ret) @@ -354,7 +360,7 @@ int drm_gem_fb_vmap(struct drm_framebuffer *fb, } if (data) { - for (i = 0; i < DRM_FORMAT_MAX_PLANES; ++i) { + for (i = 0; i < fb->format->num_planes; ++i) { memcpy(&data[i], &map[i], sizeof(data[i])); if (iosys_map_is_null(&data[i])) continue; @@ -385,10 +391,9 @@ EXPORT_SYMBOL(drm_gem_fb_vmap); * * See drm_gem_fb_vmap() for more information. */ -void drm_gem_fb_vunmap(struct drm_framebuffer *fb, - struct iosys_map map[static DRM_FORMAT_MAX_PLANES]) +void drm_gem_fb_vunmap(struct drm_framebuffer *fb, struct iosys_map *map) { - unsigned int i = DRM_FORMAT_MAX_PLANES; + unsigned int i = fb->format->num_planes; struct drm_gem_object *obj; while (i) { @@ -403,6 +408,28 @@ void drm_gem_fb_vunmap(struct drm_framebuffer *fb, } EXPORT_SYMBOL(drm_gem_fb_vunmap); +static void __drm_gem_fb_end_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir, + unsigned int num_planes) +{ + struct dma_buf_attachment *import_attach; + struct drm_gem_object *obj; + int ret; + + while (num_planes) { + --num_planes; + obj = drm_gem_fb_get_obj(fb, num_planes); + if (!obj) + continue; + import_attach = obj->import_attach; + if (!import_attach) + continue; + ret = dma_buf_end_cpu_access(import_attach->dmabuf, dir); + if (ret) + drm_err(fb->dev, "dma_buf_end_cpu_access(%u, %d) failed: %d\n", + ret, num_planes, dir); + } +} + /** * drm_gem_fb_begin_cpu_access - prepares GEM buffer objects for CPU access * @fb: the framebuffer @@ -421,40 +448,27 @@ int drm_gem_fb_begin_cpu_access(struct drm_framebuffer *fb, enum dma_data_direct { struct dma_buf_attachment *import_attach; struct drm_gem_object *obj; - size_t i; - int ret, ret2; + unsigned int i; + int ret; - for (i = 0; i < ARRAY_SIZE(fb->obj); ++i) { + for (i = 0; i < fb->format->num_planes; ++i) { obj = drm_gem_fb_get_obj(fb, i); - if (!obj) - continue; + if (!obj) { + ret = -EINVAL; + goto err___drm_gem_fb_end_cpu_access; + } import_attach = obj->import_attach; if (!import_attach) continue; ret = dma_buf_begin_cpu_access(import_attach->dmabuf, dir); if (ret) - goto err_dma_buf_end_cpu_access; + goto err___drm_gem_fb_end_cpu_access; } return 0; -err_dma_buf_end_cpu_access: - while (i) { - --i; - obj = drm_gem_fb_get_obj(fb, i); - if (!obj) - continue; - import_attach = obj->import_attach; - if (!import_attach) - continue; - ret2 = dma_buf_end_cpu_access(import_attach->dmabuf, dir); - if (ret2) { - drm_err(fb->dev, - "dma_buf_end_cpu_access() failed during error handling: %d\n", - ret2); - } - } - +err___drm_gem_fb_end_cpu_access: + __drm_gem_fb_end_cpu_access(fb, dir, i); return ret; } EXPORT_SYMBOL(drm_gem_fb_begin_cpu_access); @@ -472,23 +486,7 @@ EXPORT_SYMBOL(drm_gem_fb_begin_cpu_access); */ void drm_gem_fb_end_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir) { - size_t i = ARRAY_SIZE(fb->obj); - struct dma_buf_attachment *import_attach; - struct drm_gem_object *obj; - int ret; - - while (i) { - --i; - obj = drm_gem_fb_get_obj(fb, i); - if (!obj) - continue; - import_attach = obj->import_attach; - if (!import_attach) - continue; - ret = dma_buf_end_cpu_access(import_attach->dmabuf, dir); - if (ret) - drm_err(fb->dev, "dma_buf_end_cpu_access() failed: %d\n", ret); - } + __drm_gem_fb_end_cpu_access(fb, dir, fb->format->num_planes); } EXPORT_SYMBOL(drm_gem_fb_end_cpu_access); diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index 123045b58fec..d607043716d3 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -9,6 +9,7 @@ #include <drm/drm_file.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem_atomic_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_gem_ttm_helper.h> #include <drm/drm_gem_vram_helper.h> #include <drm/drm_managed.h> @@ -630,6 +631,24 @@ EXPORT_SYMBOL(drm_gem_vram_driver_dumb_create); * Helpers for struct drm_plane_helper_funcs */ +static void __drm_gem_vram_plane_helper_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *state, + unsigned int num_planes) +{ + struct drm_gem_object *obj; + struct drm_gem_vram_object *gbo; + struct drm_framebuffer *fb = state->fb; + + while (num_planes) { + --num_planes; + obj = drm_gem_fb_get_obj(fb, num_planes); + if (!obj) + continue; + gbo = drm_gem_vram_of_gem(obj); + drm_gem_vram_unpin(gbo); + } +} + /** * drm_gem_vram_plane_helper_prepare_fb() - \ * Implements &struct drm_plane_helper_funcs.prepare_fb @@ -648,17 +667,22 @@ int drm_gem_vram_plane_helper_prepare_fb(struct drm_plane *plane, struct drm_plane_state *new_state) { - size_t i; + struct drm_framebuffer *fb = new_state->fb; struct drm_gem_vram_object *gbo; + struct drm_gem_object *obj; + unsigned int i; int ret; - if (!new_state->fb) + if (!fb) return 0; - for (i = 0; i < ARRAY_SIZE(new_state->fb->obj); ++i) { - if (!new_state->fb->obj[i]) - continue; - gbo = drm_gem_vram_of_gem(new_state->fb->obj[i]); + for (i = 0; i < fb->format->num_planes; ++i) { + obj = drm_gem_fb_get_obj(fb, i); + if (!obj) { + ret = -EINVAL; + goto err_drm_gem_vram_unpin; + } + gbo = drm_gem_vram_of_gem(obj); ret = drm_gem_vram_pin(gbo, DRM_GEM_VRAM_PL_FLAG_VRAM); if (ret) goto err_drm_gem_vram_unpin; @@ -671,11 +695,7 @@ drm_gem_vram_plane_helper_prepare_fb(struct drm_plane *plane, return 0; err_drm_gem_vram_unpin: - while (i) { - --i; - gbo = drm_gem_vram_of_gem(new_state->fb->obj[i]); - drm_gem_vram_unpin(gbo); - } + __drm_gem_vram_plane_helper_cleanup_fb(plane, new_state, i); return ret; } EXPORT_SYMBOL(drm_gem_vram_plane_helper_prepare_fb); @@ -694,18 +714,12 @@ void drm_gem_vram_plane_helper_cleanup_fb(struct drm_plane *plane, struct drm_plane_state *old_state) { - size_t i; - struct drm_gem_vram_object *gbo; + struct drm_framebuffer *fb = old_state->fb; - if (!old_state->fb) + if (!fb) return; - for (i = 0; i < ARRAY_SIZE(old_state->fb->obj); ++i) { - if (!old_state->fb->obj[i]) - continue; - gbo = drm_gem_vram_of_gem(old_state->fb->obj[i]); - drm_gem_vram_unpin(gbo); - } + __drm_gem_vram_plane_helper_cleanup_fb(plane, old_state, fb->format->num_planes); } EXPORT_SYMBOL(drm_gem_vram_plane_helper_cleanup_fb); diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index 9314f2ead79f..09e4edb5a992 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -1199,6 +1199,13 @@ int mipi_dbi_spi_transfer(struct spi_device *spi, u32 speed_hz, size_t chunk; int ret; + /* In __spi_validate, there's a validation that no partial transfers + * are accepted (xfer->len % w_size must be zero). + * Here we align max_chunk to multiple of 2 (16bits), + * to prevent transfers from being rejected. + */ + max_chunk = ALIGN_DOWN(max_chunk, 2); + spi_message_init_with_transfers(&m, &tr, 1); while (len) { diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 14b746f7ba97..40b7b245e98c 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1328,6 +1328,10 @@ void drm_mode_prune_invalid(struct drm_device *dev, list_for_each_entry_safe(mode, t, mode_list, head) { if (mode->status != MODE_OK) { list_del(&mode->head); + if (mode->type & DRM_MODE_TYPE_USERDEF) { + drm_warn(dev, "User-defined mode not supported: " + DRM_MODE_FMT "\n", DRM_MODE_ARG(mode)); + } if (verbose) { drm_mode_debug_printmodeline(mode); DRM_DEBUG_KMS("Not using %s mode: %s\n", diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 682359512996..75a71649b64d 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -354,6 +354,61 @@ drm_helper_probe_detect(struct drm_connector *connector, } EXPORT_SYMBOL(drm_helper_probe_detect); +static int __drm_helper_update_and_validate(struct drm_connector *connector, + uint32_t maxX, uint32_t maxY, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_device *dev = connector->dev; + struct drm_display_mode *mode; + int mode_flags = 0; + int ret; + + drm_connector_list_update(connector); + + if (connector->interlace_allowed) + mode_flags |= DRM_MODE_FLAG_INTERLACE; + if (connector->doublescan_allowed) + mode_flags |= DRM_MODE_FLAG_DBLSCAN; + if (connector->stereo_allowed) + mode_flags |= DRM_MODE_FLAG_3D_MASK; + + list_for_each_entry(mode, &connector->modes, head) { + if (mode->status != MODE_OK) + continue; + + mode->status = drm_mode_validate_driver(dev, mode); + if (mode->status != MODE_OK) + continue; + + mode->status = drm_mode_validate_size(mode, maxX, maxY); + if (mode->status != MODE_OK) + continue; + + mode->status = drm_mode_validate_flag(mode, mode_flags); + if (mode->status != MODE_OK) + continue; + + ret = drm_mode_validate_pipeline(mode, connector, ctx, + &mode->status); + if (ret) { + drm_dbg_kms(dev, + "drm_mode_validate_pipeline failed: %d\n", + ret); + + if (drm_WARN_ON_ONCE(dev, ret != -EDEADLK)) + mode->status = MODE_ERROR; + else + return -EDEADLK; + } + + if (mode->status != MODE_OK) + continue; + mode->status = drm_mode_validate_ycbcr420(mode, connector); + } + + return 0; +} + /** * drm_helper_probe_single_connector_modes - get complete set of display modes * @connector: connector to probe @@ -421,8 +476,6 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, const struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; int count = 0, ret; - int mode_flags = 0; - bool verbose_prune = true; enum drm_connector_status old_status; struct drm_modeset_acquire_ctx ctx; @@ -502,8 +555,8 @@ retry: DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n", connector->base.id, connector->name); drm_connector_update_edid_property(connector, NULL); - verbose_prune = false; - goto prune; + drm_mode_prune_invalid(dev, &connector->modes, false); + goto exit; } count = (*connector_funcs->get_modes)(connector); @@ -516,60 +569,47 @@ retry: count = drm_add_override_edid_modes(connector); if (count == 0 && (connector->status == connector_status_connected || - connector->status == connector_status_unknown)) + connector->status == connector_status_unknown)) { count = drm_add_modes_noedid(connector, 1024, 768); - count += drm_helper_probe_add_cmdline_mode(connector); - if (count == 0) - goto prune; - - drm_connector_list_update(connector); - - if (connector->interlace_allowed) - mode_flags |= DRM_MODE_FLAG_INTERLACE; - if (connector->doublescan_allowed) - mode_flags |= DRM_MODE_FLAG_DBLSCAN; - if (connector->stereo_allowed) - mode_flags |= DRM_MODE_FLAG_3D_MASK; - - list_for_each_entry(mode, &connector->modes, head) { - if (mode->status != MODE_OK) - continue; - mode->status = drm_mode_validate_driver(dev, mode); - if (mode->status != MODE_OK) - continue; - - mode->status = drm_mode_validate_size(mode, maxX, maxY); - if (mode->status != MODE_OK) - continue; - - mode->status = drm_mode_validate_flag(mode, mode_flags); - if (mode->status != MODE_OK) - continue; + /* + * Section 4.2.2.6 (EDID Corruption Detection) of the DP 1.4a + * Link CTS specifies that 640x480 (the official "failsafe" + * mode) needs to be the default if there's no EDID. + */ + if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) + drm_set_preferred_mode(connector, 640, 480); + } + count += drm_helper_probe_add_cmdline_mode(connector); + if (count != 0) { + ret = __drm_helper_update_and_validate(connector, maxX, maxY, &ctx); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } + } - ret = drm_mode_validate_pipeline(mode, connector, &ctx, - &mode->status); - if (ret) { - drm_dbg_kms(dev, - "drm_mode_validate_pipeline failed: %d\n", - ret); + drm_mode_prune_invalid(dev, &connector->modes, true); - if (drm_WARN_ON_ONCE(dev, ret != -EDEADLK)) { - mode->status = MODE_ERROR; - } else { - drm_modeset_backoff(&ctx); - goto retry; - } + /* + * Displayport spec section 5.2.1.2 ("Video Timing Format") says that + * all detachable sinks shall support 640x480 @60Hz as a fail safe + * mode. If all modes were pruned, perhaps because they need more + * lanes or a higher pixel clock than available, at least try to add + * in 640x480. + */ + if (list_empty(&connector->modes) && + connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) { + count = drm_add_modes_noedid(connector, 640, 480); + ret = __drm_helper_update_and_validate(connector, maxX, maxY, &ctx); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; } - - if (mode->status != MODE_OK) - continue; - mode->status = drm_mode_validate_ycbcr420(mode, connector); + drm_mode_prune_invalid(dev, &connector->modes, true); } -prune: - drm_mode_prune_invalid(dev, &connector->modes, verbose_prune); - +exit: drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); @@ -964,3 +1004,39 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev) return changed; } EXPORT_SYMBOL(drm_helper_hpd_irq_event); + +/** + * drm_connector_helper_get_modes_from_ddc - Updates the connector's EDID + * property from the connector's + * DDC channel + * @connector: The connector + * + * Returns: + * The number of detected display modes. + * + * Uses a connector's DDC channel to retrieve EDID data and update the + * connector's EDID property and display modes. Drivers can use this + * function to implement struct &drm_connector_helper_funcs.get_modes + * for connectors with a DDC channel. + */ +int drm_connector_helper_get_modes_from_ddc(struct drm_connector *connector) +{ + struct edid *edid; + int count = 0; + + if (!connector->ddc) + return 0; + + edid = drm_get_edid(connector, connector->ddc); + + // clears property if EDID is NULL + drm_connector_update_edid_property(connector, edid); + + if (edid) { + count = drm_add_edid_modes(connector, edid); + kfree(edid); + } + + return count; +} +EXPORT_SYMBOL(drm_connector_helper_get_modes_from_ddc); diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index 7e48dcd1bee4..bbad9e4696e7 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -184,6 +184,7 @@ */ #include <linux/anon_inodes.h> +#include <linux/dma-fence-unwrap.h> #include <linux/file.h> #include <linux/fs.h> #include <linux/sched/signal.h> @@ -853,57 +854,12 @@ drm_syncobj_fd_to_handle_ioctl(struct drm_device *dev, void *data, &args->handle); } - -/* - * Try to flatten a dma_fence_chain into a dma_fence_array so that it can be - * added as timeline fence to a chain again. - */ -static int drm_syncobj_flatten_chain(struct dma_fence **f) -{ - struct dma_fence_chain *chain = to_dma_fence_chain(*f); - struct dma_fence *tmp, **fences; - struct dma_fence_array *array; - unsigned int count; - - if (!chain) - return 0; - - count = 0; - dma_fence_chain_for_each(tmp, &chain->base) - ++count; - - fences = kmalloc_array(count, sizeof(*fences), GFP_KERNEL); - if (!fences) - return -ENOMEM; - - count = 0; - dma_fence_chain_for_each(tmp, &chain->base) - fences[count++] = dma_fence_get(tmp); - - array = dma_fence_array_create(count, fences, - dma_fence_context_alloc(1), - 1, false); - if (!array) - goto free_fences; - - dma_fence_put(*f); - *f = &array->base; - return 0; - -free_fences: - while (count--) - dma_fence_put(fences[count]); - - kfree(fences); - return -ENOMEM; -} - static int drm_syncobj_transfer_to_timeline(struct drm_file *file_private, struct drm_syncobj_transfer *args) { struct drm_syncobj *timeline_syncobj = NULL; + struct dma_fence *fence, *tmp; struct dma_fence_chain *chain; - struct dma_fence *fence; int ret; timeline_syncobj = drm_syncobj_find(file_private, args->dst_handle); @@ -912,13 +868,14 @@ static int drm_syncobj_transfer_to_timeline(struct drm_file *file_private, } ret = drm_syncobj_find_fence(file_private, args->src_handle, args->src_point, args->flags, - &fence); + &tmp); if (ret) goto err_put_timeline; - ret = drm_syncobj_flatten_chain(&fence); - if (ret) - goto err_free_fence; + fence = dma_fence_unwrap_merge(tmp); + dma_fence_put(tmp); + if (!fence) + goto err_put_timeline; chain = dma_fence_chain_alloc(); if (!chain) { diff --git a/drivers/gpu/drm/gma500/cdv_intel_crt.c b/drivers/gpu/drm/gma500/cdv_intel_crt.c index 6bcd18c63c31..7ff1e5141150 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_crt.c +++ b/drivers/gpu/drm/gma500/cdv_intel_crt.c @@ -192,18 +192,16 @@ static enum drm_connector_status cdv_intel_crt_detect( static void cdv_intel_crt_destroy(struct drm_connector *connector) { struct gma_connector *gma_connector = to_gma_connector(connector); - struct gma_encoder *gma_encoder = gma_attached_encoder(connector); + struct gma_i2c_chan *ddc_bus = to_gma_i2c_chan(connector->ddc); - psb_intel_i2c_destroy(gma_encoder->ddc_bus); + gma_i2c_destroy(ddc_bus); drm_connector_cleanup(connector); kfree(gma_connector); } static int cdv_intel_crt_get_modes(struct drm_connector *connector) { - struct gma_encoder *gma_encoder = gma_attached_encoder(connector); - return psb_intel_ddc_get_modes(connector, - &gma_encoder->ddc_bus->adapter); + return psb_intel_ddc_get_modes(connector, connector->ddc); } static int cdv_intel_crt_set_property(struct drm_connector *connector, @@ -245,8 +243,10 @@ void cdv_intel_crt_init(struct drm_device *dev, struct gma_connector *gma_connector; struct gma_encoder *gma_encoder; + struct gma_i2c_chan *ddc_bus; struct drm_connector *connector; struct drm_encoder *encoder; + int ret; gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL); if (!gma_encoder) @@ -254,25 +254,31 @@ void cdv_intel_crt_init(struct drm_device *dev, gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); if (!gma_connector) - goto failed_connector; + goto err_free_encoder; + + /* Set up the DDC bus. */ + ddc_bus = gma_i2c_create(dev, GPIOA, "CRTDDC_A"); + if (!ddc_bus) { + dev_printk(KERN_ERR, dev->dev, "DDC bus registration failed.\n"); + goto err_free_connector; + } connector = &gma_connector->base; connector->polled = DRM_CONNECTOR_POLL_HPD; - drm_connector_init(dev, connector, - &cdv_intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); + ret = drm_connector_init_with_ddc(dev, connector, + &cdv_intel_crt_connector_funcs, + DRM_MODE_CONNECTOR_VGA, + &ddc_bus->base); + if (ret) + goto err_ddc_destroy; encoder = &gma_encoder->base; - drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_DAC); + ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_DAC); + if (ret) + goto err_connector_cleanup; gma_connector_attach_encoder(gma_connector, gma_encoder); - /* Set up the DDC bus. */ - gma_encoder->ddc_bus = psb_intel_i2c_create(dev, GPIOA, "CRTDDC_A"); - if (!gma_encoder->ddc_bus) { - dev_printk(KERN_ERR, dev->dev, "DDC bus registration failed.\n"); - goto failed_ddc; - } - gma_encoder->type = INTEL_OUTPUT_ANALOG; connector->interlace_allowed = 0; connector->doublescan_allowed = 0; @@ -282,11 +288,14 @@ void cdv_intel_crt_init(struct drm_device *dev, &cdv_intel_crt_connector_helper_funcs); return; -failed_ddc: - drm_encoder_cleanup(&gma_encoder->base); + +err_connector_cleanup: drm_connector_cleanup(&gma_connector->base); +err_ddc_destroy: + gma_i2c_destroy(ddc_bus); +err_free_connector: kfree(gma_connector); -failed_connector: +err_free_encoder: kfree(gma_encoder); return; } diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c index 8987e555e113..29ef45f14169 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c +++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c @@ -53,7 +53,6 @@ struct mid_intel_hdmi_priv { bool has_hdmi_audio; /* Should set this when detect hotplug */ bool hdmi_device_connected; - struct i2c_adapter *hdmi_i2c_adapter; /* for control functions */ struct drm_device *dev; }; @@ -130,7 +129,7 @@ static enum drm_connector_status cdv_hdmi_detect( struct edid *edid = NULL; enum drm_connector_status status = connector_status_disconnected; - edid = drm_get_edid(connector, &gma_encoder->i2c_bus->adapter); + edid = drm_get_edid(connector, connector->ddc); hdmi_priv->has_hdmi_sink = false; hdmi_priv->has_hdmi_audio = false; @@ -208,11 +207,10 @@ static int cdv_hdmi_set_property(struct drm_connector *connector, */ static int cdv_hdmi_get_modes(struct drm_connector *connector) { - struct gma_encoder *gma_encoder = gma_attached_encoder(connector); struct edid *edid = NULL; int ret = 0; - edid = drm_get_edid(connector, &gma_encoder->i2c_bus->adapter); + edid = drm_get_edid(connector, connector->ddc); if (edid) { drm_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); @@ -243,9 +241,9 @@ static enum drm_mode_status cdv_hdmi_mode_valid(struct drm_connector *connector, static void cdv_hdmi_destroy(struct drm_connector *connector) { struct gma_connector *gma_connector = to_gma_connector(connector); - struct gma_encoder *gma_encoder = gma_attached_encoder(connector); + struct gma_i2c_chan *ddc_bus = to_gma_i2c_chan(connector->ddc); - psb_intel_i2c_destroy(gma_encoder->i2c_bus); + gma_i2c_destroy(ddc_bus); drm_connector_cleanup(connector); kfree(gma_connector); } @@ -278,37 +276,60 @@ void cdv_hdmi_init(struct drm_device *dev, struct gma_encoder *gma_encoder; struct gma_connector *gma_connector; struct drm_connector *connector; - struct drm_encoder *encoder; struct mid_intel_hdmi_priv *hdmi_priv; - int ddc_bus; + struct gma_i2c_chan *ddc_bus; + int ddc_reg; + int ret; gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL); - if (!gma_encoder) return; - gma_connector = kzalloc(sizeof(struct gma_connector), - GFP_KERNEL); - + gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); if (!gma_connector) - goto err_connector; + goto err_free_encoder; hdmi_priv = kzalloc(sizeof(struct mid_intel_hdmi_priv), GFP_KERNEL); - if (!hdmi_priv) - goto err_priv; + goto err_free_connector; connector = &gma_connector->base; connector->polled = DRM_CONNECTOR_POLL_HPD; gma_connector->save = cdv_hdmi_save; gma_connector->restore = cdv_hdmi_restore; - encoder = &gma_encoder->base; - drm_connector_init(dev, connector, - &cdv_hdmi_connector_funcs, - DRM_MODE_CONNECTOR_DVID); + switch (reg) { + case SDVOB: + ddc_reg = GPIOE; + gma_encoder->ddi_select = DDI0_SELECT; + break; + case SDVOC: + ddc_reg = GPIOD; + gma_encoder->ddi_select = DDI1_SELECT; + break; + default: + DRM_ERROR("unknown reg 0x%x for HDMI\n", reg); + goto err_free_hdmi_priv; + } + + ddc_bus = gma_i2c_create(dev, ddc_reg, + (reg == SDVOB) ? "HDMIB" : "HDMIC"); + if (!ddc_bus) { + dev_err(dev->dev, "No ddc adapter available!\n"); + goto err_free_hdmi_priv; + } - drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_TMDS); + ret = drm_connector_init_with_ddc(dev, connector, + &cdv_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_DVID, + &ddc_bus->base); + if (ret) + goto err_ddc_destroy; + + ret = drm_simple_encoder_init(dev, &gma_encoder->base, + DRM_MODE_ENCODER_TMDS); + if (ret) + goto err_connector_cleanup; gma_connector_attach_encoder(gma_connector, gma_encoder); gma_encoder->type = INTEL_OUTPUT_HDMI; @@ -316,7 +337,7 @@ void cdv_hdmi_init(struct drm_device *dev, hdmi_priv->has_hdmi_sink = false; gma_encoder->dev_priv = hdmi_priv; - drm_encoder_helper_add(encoder, &cdv_hdmi_helper_funcs); + drm_encoder_helper_add(&gma_encoder->base, &cdv_hdmi_helper_funcs); drm_connector_helper_add(connector, &cdv_hdmi_connector_helper_funcs); connector->display_info.subpixel_order = SubPixelHorizontalRGB; @@ -327,38 +348,17 @@ void cdv_hdmi_init(struct drm_device *dev, dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN); - switch (reg) { - case SDVOB: - ddc_bus = GPIOE; - gma_encoder->ddi_select = DDI0_SELECT; - break; - case SDVOC: - ddc_bus = GPIOD; - gma_encoder->ddi_select = DDI1_SELECT; - break; - default: - DRM_ERROR("unknown reg 0x%x for HDMI\n", reg); - goto failed_ddc; - break; - } - - gma_encoder->i2c_bus = psb_intel_i2c_create(dev, - ddc_bus, (reg == SDVOB) ? "HDMIB" : "HDMIC"); - - if (!gma_encoder->i2c_bus) { - dev_err(dev->dev, "No ddc adapter available!\n"); - goto failed_ddc; - } - - hdmi_priv->hdmi_i2c_adapter = &(gma_encoder->i2c_bus->adapter); hdmi_priv->dev = dev; return; -failed_ddc: - drm_encoder_cleanup(encoder); +err_connector_cleanup: drm_connector_cleanup(connector); -err_priv: +err_ddc_destroy: + gma_i2c_destroy(ddc_bus); +err_free_hdmi_priv: + kfree(hdmi_priv); +err_free_connector: kfree(gma_connector); -err_connector: +err_free_encoder: kfree(gma_encoder); } diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c index 98d9f5483a7c..be6efcaaa3b3 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c +++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c @@ -298,11 +298,10 @@ static int cdv_intel_lvds_get_modes(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); - struct gma_encoder *gma_encoder = gma_attached_encoder(connector); struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; int ret; - ret = psb_intel_ddc_get_modes(connector, &gma_encoder->i2c_bus->adapter); + ret = psb_intel_ddc_get_modes(connector, connector->ddc); if (ret) return ret; @@ -317,19 +316,13 @@ static int cdv_intel_lvds_get_modes(struct drm_connector *connector) return 0; } -/** - * cdv_intel_lvds_destroy - unregister and free LVDS structures - * @connector: connector to free - * - * Unregister the DDC bus for this connector then free the driver private - * structure. - */ static void cdv_intel_lvds_destroy(struct drm_connector *connector) { struct gma_connector *gma_connector = to_gma_connector(connector); struct gma_encoder *gma_encoder = gma_attached_encoder(connector); - psb_intel_i2c_destroy(gma_encoder->i2c_bus); + gma_i2c_destroy(to_gma_i2c_chan(connector->ddc)); + gma_i2c_destroy(gma_encoder->i2c_bus); drm_connector_cleanup(connector); kfree(gma_connector); } @@ -487,8 +480,10 @@ void cdv_intel_lvds_init(struct drm_device *dev, struct drm_display_mode *scan; struct drm_crtc *crtc; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + struct gma_i2c_chan *ddc_bus; u32 lvds; int pipe; + int ret; u8 pin; if (!dev_priv->lvds_enabled_in_vbt) @@ -508,11 +503,11 @@ void cdv_intel_lvds_init(struct drm_device *dev, gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); if (!gma_connector) - goto failed_connector; + goto err_free_encoder; lvds_priv = kzalloc(sizeof(struct cdv_intel_lvds_priv), GFP_KERNEL); if (!lvds_priv) - goto failed_lvds_priv; + goto err_free_connector; gma_encoder->dev_priv = lvds_priv; @@ -521,12 +516,24 @@ void cdv_intel_lvds_init(struct drm_device *dev, gma_connector->restore = cdv_intel_lvds_restore; encoder = &gma_encoder->base; + /* Set up the DDC bus. */ + ddc_bus = gma_i2c_create(dev, GPIOC, "LVDSDDC_C"); + if (!ddc_bus) { + dev_printk(KERN_ERR, dev->dev, + "DDC bus registration " "failed.\n"); + goto err_free_lvds_priv; + } - drm_connector_init(dev, connector, - &cdv_intel_lvds_connector_funcs, - DRM_MODE_CONNECTOR_LVDS); + ret = drm_connector_init_with_ddc(dev, connector, + &cdv_intel_lvds_connector_funcs, + DRM_MODE_CONNECTOR_LVDS, + &ddc_bus->base); + if (ret) + goto err_destroy_ddc; - drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS); + ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS); + if (ret) + goto err_connector_cleanup; gma_connector_attach_encoder(gma_connector, gma_encoder); gma_encoder->type = INTEL_OUTPUT_LVDS; @@ -550,13 +557,11 @@ void cdv_intel_lvds_init(struct drm_device *dev, * Set up I2C bus * FIXME: distroy i2c_bus when exit */ - gma_encoder->i2c_bus = psb_intel_i2c_create(dev, - GPIOB, - "LVDSBLC_B"); + gma_encoder->i2c_bus = gma_i2c_create(dev, GPIOB, "LVDSBLC_B"); if (!gma_encoder->i2c_bus) { dev_printk(KERN_ERR, dev->dev, "I2C bus registration failed.\n"); - goto failed_blc_i2c; + goto err_encoder_cleanup; } gma_encoder->i2c_bus->slave_addr = 0x2C; dev_priv->lvds_i2c_bus = gma_encoder->i2c_bus; @@ -571,23 +576,13 @@ void cdv_intel_lvds_init(struct drm_device *dev, * if closed, act like it's not there for now */ - /* Set up the DDC bus. */ - gma_encoder->ddc_bus = psb_intel_i2c_create(dev, - GPIOC, - "LVDSDDC_C"); - if (!gma_encoder->ddc_bus) { - dev_printk(KERN_ERR, dev->dev, - "DDC bus registration " "failed.\n"); - goto failed_ddc; - } - /* * Attempt to get the fixed panel mode from DDC. Assume that the * preferred mode is the right one. */ mutex_lock(&dev->mode_config.mutex); - psb_intel_ddc_get_modes(connector, - &gma_encoder->ddc_bus->adapter); + psb_intel_ddc_get_modes(connector, &ddc_bus->base); + list_for_each_entry(scan, &connector->probed_modes, head) { if (scan->type & DRM_MODE_TYPE_PREFERRED) { mode_dev->panel_fixed_mode = @@ -629,7 +624,7 @@ void cdv_intel_lvds_init(struct drm_device *dev, if (!mode_dev->panel_fixed_mode) { DRM_DEBUG ("Found no modes on the lvds, ignoring the LVDS\n"); - goto failed_find; + goto err_unlock; } /* setup PWM */ @@ -649,20 +644,19 @@ out: mutex_unlock(&dev->mode_config.mutex); return; -failed_find: +err_unlock: mutex_unlock(&dev->mode_config.mutex); - pr_err("Failed find\n"); - psb_intel_i2c_destroy(gma_encoder->ddc_bus); -failed_ddc: - pr_err("Failed DDC\n"); - psb_intel_i2c_destroy(gma_encoder->i2c_bus); -failed_blc_i2c: - pr_err("Failed BLC\n"); + gma_i2c_destroy(gma_encoder->i2c_bus); +err_encoder_cleanup: drm_encoder_cleanup(encoder); +err_connector_cleanup: drm_connector_cleanup(connector); +err_destroy_ddc: + gma_i2c_destroy(ddc_bus); +err_free_lvds_priv: kfree(lvds_priv); -failed_lvds_priv: +err_free_connector: kfree(gma_connector); -failed_connector: +err_free_encoder: kfree(gma_encoder); } diff --git a/drivers/gpu/drm/gma500/intel_i2c.c b/drivers/gpu/drm/gma500/intel_i2c.c index 5e1b4d70c317..9d02a7b6d9a3 100644 --- a/drivers/gpu/drm/gma500/intel_i2c.c +++ b/drivers/gpu/drm/gma500/intel_i2c.c @@ -22,7 +22,7 @@ static int get_clock(void *data) { - struct psb_intel_i2c_chan *chan = data; + struct gma_i2c_chan *chan = data; struct drm_device *dev = chan->drm_dev; u32 val; @@ -32,7 +32,7 @@ static int get_clock(void *data) static int get_data(void *data) { - struct psb_intel_i2c_chan *chan = data; + struct gma_i2c_chan *chan = data; struct drm_device *dev = chan->drm_dev; u32 val; @@ -42,7 +42,7 @@ static int get_data(void *data) static void set_clock(void *data, int state_high) { - struct psb_intel_i2c_chan *chan = data; + struct gma_i2c_chan *chan = data; struct drm_device *dev = chan->drm_dev; u32 reserved = 0, clock_bits; @@ -62,7 +62,7 @@ static void set_clock(void *data, int state_high) static void set_data(void *data, int state_high) { - struct psb_intel_i2c_chan *chan = data; + struct gma_i2c_chan *chan = data; struct drm_device *dev = chan->drm_dev; u32 reserved = 0, data_bits; @@ -83,7 +83,7 @@ static void set_data(void *data, int state_high) } /** - * psb_intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg + * gma_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg * @dev: DRM device * @reg: GPIO reg to use * @name: name for this bus @@ -102,21 +102,21 @@ static void set_data(void *data, int state_high) * %GPIOH * see PRM for details on how these different busses are used. */ -struct psb_intel_i2c_chan *psb_intel_i2c_create(struct drm_device *dev, - const u32 reg, const char *name) +struct gma_i2c_chan *gma_i2c_create(struct drm_device *dev, const u32 reg, + const char *name) { - struct psb_intel_i2c_chan *chan; + struct gma_i2c_chan *chan; - chan = kzalloc(sizeof(struct psb_intel_i2c_chan), GFP_KERNEL); + chan = kzalloc(sizeof(struct gma_i2c_chan), GFP_KERNEL); if (!chan) goto out_free; chan->drm_dev = dev; chan->reg = reg; - snprintf(chan->adapter.name, I2C_NAME_SIZE, "intel drm %s", name); - chan->adapter.owner = THIS_MODULE; - chan->adapter.algo_data = &chan->algo; - chan->adapter.dev.parent = dev->dev; + snprintf(chan->base.name, I2C_NAME_SIZE, "intel drm %s", name); + chan->base.owner = THIS_MODULE; + chan->base.algo_data = &chan->algo; + chan->base.dev.parent = dev->dev; chan->algo.setsda = set_data; chan->algo.setscl = set_clock; chan->algo.getsda = get_data; @@ -125,9 +125,9 @@ struct psb_intel_i2c_chan *psb_intel_i2c_create(struct drm_device *dev, chan->algo.timeout = usecs_to_jiffies(2200); chan->algo.data = chan; - i2c_set_adapdata(&chan->adapter, chan); + i2c_set_adapdata(&chan->base, chan); - if (i2c_bit_add_bus(&chan->adapter)) + if (i2c_bit_add_bus(&chan->base)) goto out_free; /* JJJ: raise SCL and SDA? */ @@ -143,16 +143,16 @@ out_free: } /** - * psb_intel_i2c_destroy - unregister and free i2c bus resources + * gma_i2c_destroy - unregister and free i2c bus resources * @chan: channel to free * * Unregister the adapter from the i2c layer, then free the structure. */ -void psb_intel_i2c_destroy(struct psb_intel_i2c_chan *chan) +void gma_i2c_destroy(struct gma_i2c_chan *chan) { if (!chan) return; - i2c_del_adapter(&chan->adapter); + i2c_del_adapter(&chan->base); kfree(chan); } diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c index aed5de8f8245..9c9ebf8e29c4 100644 --- a/drivers/gpu/drm/gma500/oaktrail_lvds.c +++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c @@ -293,12 +293,14 @@ void oaktrail_lvds_init(struct drm_device *dev, { struct gma_encoder *gma_encoder; struct gma_connector *gma_connector; + struct gma_i2c_chan *ddc_bus; struct drm_connector *connector; struct drm_encoder *encoder; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct edid *edid; struct i2c_adapter *i2c_adap; struct drm_display_mode *scan; /* *modes, *bios_mode; */ + int ret; gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL); if (!gma_encoder) @@ -306,16 +308,20 @@ void oaktrail_lvds_init(struct drm_device *dev, gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); if (!gma_connector) - goto failed_connector; + goto err_free_encoder; connector = &gma_connector->base; encoder = &gma_encoder->base; dev_priv->is_lvds_on = true; - drm_connector_init(dev, connector, - &psb_intel_lvds_connector_funcs, - DRM_MODE_CONNECTOR_LVDS); + ret = drm_connector_init(dev, connector, + &psb_intel_lvds_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + if (ret) + goto err_free_connector; - drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS); + ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS); + if (ret) + goto err_connector_cleanup; gma_connector_attach_encoder(gma_connector, gma_encoder); gma_encoder->type = INTEL_OUTPUT_LVDS; @@ -353,16 +359,26 @@ void oaktrail_lvds_init(struct drm_device *dev, edid = NULL; mutex_lock(&dev->mode_config.mutex); + i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus); if (i2c_adap) edid = drm_get_edid(connector, i2c_adap); + if (edid == NULL && dev_priv->lpc_gpio_base) { - oaktrail_lvds_i2c_init(encoder); - if (gma_encoder->ddc_bus != NULL) { - i2c_adap = &gma_encoder->ddc_bus->adapter; + ddc_bus = oaktrail_lvds_i2c_init(dev); + if (!IS_ERR(ddc_bus)) { + i2c_adap = &ddc_bus->base; edid = drm_get_edid(connector, i2c_adap); } } + + /* + * Due to the logic in probing for i2c buses above we do not know the + * i2c_adap until now. Hence we cannot use drm_connector_init_with_ddc() + * but must instead set connector->ddc manually here. + */ + connector->ddc = i2c_adap; + /* * Attempt to get the fixed panel mode from DDC. Assume that the * preferred mode is the right one. @@ -395,7 +411,7 @@ void oaktrail_lvds_init(struct drm_device *dev, /* If we still don't have a mode after all that, give up. */ if (!mode_dev->panel_fixed_mode) { dev_err(dev->dev, "Found no modes on the lvds, ignoring the LVDS\n"); - goto failed_find; + goto err_unlock; } out: @@ -403,21 +419,15 @@ out: return; -failed_find: +err_unlock: mutex_unlock(&dev->mode_config.mutex); - - dev_dbg(dev->dev, "No LVDS modes found, disabling.\n"); - if (gma_encoder->ddc_bus) { - psb_intel_i2c_destroy(gma_encoder->ddc_bus); - gma_encoder->ddc_bus = NULL; - } - -/* failed_ddc: */ - + gma_i2c_destroy(to_gma_i2c_chan(connector->ddc)); drm_encoder_cleanup(encoder); +err_connector_cleanup: drm_connector_cleanup(connector); +err_free_connector: kfree(gma_connector); -failed_connector: +err_free_encoder: kfree(gma_encoder); } diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds_i2c.c b/drivers/gpu/drm/gma500/oaktrail_lvds_i2c.c index d1ae91fcd224..06b5b2d70d48 100644 --- a/drivers/gpu/drm/gma500/oaktrail_lvds_i2c.c +++ b/drivers/gpu/drm/gma500/oaktrail_lvds_i2c.c @@ -65,7 +65,7 @@ static int get_clock(void *data) { - struct psb_intel_i2c_chan *chan = data; + struct gma_i2c_chan *chan = data; u32 val; val = LPC_READ_REG(chan, RGIO); @@ -79,7 +79,7 @@ static int get_clock(void *data) static int get_data(void *data) { - struct psb_intel_i2c_chan *chan = data; + struct gma_i2c_chan *chan = data; u32 val; val = LPC_READ_REG(chan, RGIO); @@ -93,7 +93,7 @@ static int get_data(void *data) static void set_clock(void *data, int state_high) { - struct psb_intel_i2c_chan *chan = data; + struct gma_i2c_chan *chan = data; u32 val; if (state_high) { @@ -112,7 +112,7 @@ static void set_clock(void *data, int state_high) static void set_data(void *data, int state_high) { - struct psb_intel_i2c_chan *chan = data; + struct gma_i2c_chan *chan = data; u32 val; if (state_high) { @@ -129,23 +129,22 @@ static void set_data(void *data, int state_high) } } -void oaktrail_lvds_i2c_init(struct drm_encoder *encoder) +struct gma_i2c_chan *oaktrail_lvds_i2c_init(struct drm_device *dev) { - struct drm_device *dev = encoder->dev; - struct gma_encoder *gma_encoder = to_gma_encoder(encoder); struct drm_psb_private *dev_priv = to_drm_psb_private(dev); - struct psb_intel_i2c_chan *chan; + struct gma_i2c_chan *chan; + int ret; - chan = kzalloc(sizeof(struct psb_intel_i2c_chan), GFP_KERNEL); + chan = kzalloc(sizeof(struct gma_i2c_chan), GFP_KERNEL); if (!chan) - return; + return ERR_PTR(-ENOMEM); chan->drm_dev = dev; chan->reg = dev_priv->lpc_gpio_base; - strncpy(chan->adapter.name, "gma500 LPC", I2C_NAME_SIZE - 1); - chan->adapter.owner = THIS_MODULE; - chan->adapter.algo_data = &chan->algo; - chan->adapter.dev.parent = dev->dev; + strncpy(chan->base.name, "gma500 LPC", I2C_NAME_SIZE - 1); + chan->base.owner = THIS_MODULE; + chan->base.algo_data = &chan->algo; + chan->base.dev.parent = dev->dev; chan->algo.setsda = set_data; chan->algo.setscl = set_clock; chan->algo.getsda = get_data; @@ -154,16 +153,17 @@ void oaktrail_lvds_i2c_init(struct drm_encoder *encoder) chan->algo.timeout = usecs_to_jiffies(2200); chan->algo.data = chan; - i2c_set_adapdata(&chan->adapter, chan); + i2c_set_adapdata(&chan->base, chan); set_data(chan, 1); set_clock(chan, 1); udelay(50); - if (i2c_bit_add_bus(&chan->adapter)) { + ret = i2c_bit_add_bus(&chan->base); + if (ret < 0) { kfree(chan); - return; + return ERR_PTR(ret); } - gma_encoder->ddc_bus = chan; + return chan; } diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index 0ddfec1a0851..0ea3d23575f3 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -469,7 +469,7 @@ struct drm_psb_private { struct drm_display_mode *sdvo_lvds_vbt_mode; struct bdb_lvds_backlight *lvds_bl; /* LVDS backlight info from VBT */ - struct psb_intel_i2c_chan *lvds_i2c_bus; /* FIXME: Remove this? */ + struct gma_i2c_chan *lvds_i2c_bus; /* FIXME: Remove this? */ /* Feature bits from the VBIOS */ unsigned int int_tv_support:1; diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index db3e757328fe..8ccba116821b 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -78,13 +78,14 @@ struct psb_intel_mode_device { uint32_t saveBLC_PWM_CTL; }; -struct psb_intel_i2c_chan { - /* for getting at dev. private (mmio etc.) */ - struct drm_device *drm_dev; - u32 reg; /* GPIO reg */ - struct i2c_adapter adapter; +struct gma_i2c_chan { + struct i2c_adapter base; struct i2c_algo_bit_data algo; u8 slave_addr; + + /* for getting at dev. private (mmio etc.) */ + struct drm_device *drm_dev; + u32 reg; /* GPIO reg */ }; struct gma_encoder { @@ -103,8 +104,7 @@ struct gma_encoder { /* FIXME: Either make SDVO and LVDS store it's i2c here or give CDV it's own set of output privates */ - struct psb_intel_i2c_chan *i2c_bus; - struct psb_intel_i2c_chan *ddc_bus; + struct gma_i2c_chan *i2c_bus; }; struct gma_connector { @@ -175,10 +175,12 @@ struct gma_crtc { container_of(x, struct gma_encoder, base) #define to_psb_intel_framebuffer(x) \ container_of(x, struct psb_intel_framebuffer, base) +#define to_gma_i2c_chan(x) \ + container_of(x, struct gma_i2c_chan, base) -struct psb_intel_i2c_chan *psb_intel_i2c_create(struct drm_device *dev, - const u32 reg, const char *name); -void psb_intel_i2c_destroy(struct psb_intel_i2c_chan *chan); +struct gma_i2c_chan *gma_i2c_create(struct drm_device *dev, const u32 reg, + const char *name); +void gma_i2c_destroy(struct gma_i2c_chan *chan); int psb_intel_ddc_get_modes(struct drm_connector *connector, struct i2c_adapter *adapter); extern bool psb_intel_ddc_probe(struct i2c_adapter *adapter); @@ -197,7 +199,7 @@ extern void oaktrail_lvds_init(struct drm_device *dev, extern void oaktrail_wait_for_INTR_PKT_SENT(struct drm_device *dev); extern void oaktrail_dsi_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev); -extern void oaktrail_lvds_i2c_init(struct drm_encoder *encoder); +struct gma_i2c_chan *oaktrail_lvds_i2c_init(struct drm_device *dev); extern void mid_dsi_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int dsi_num); diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c index cad00380b386..7ee6c8ce103b 100644 --- a/drivers/gpu/drm/gma500/psb_intel_lvds.c +++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c @@ -49,8 +49,7 @@ struct psb_intel_lvds_priv { uint32_t savePFIT_PGM_RATIOS; uint32_t saveBLC_PWM_CTL; - struct psb_intel_i2c_chan *i2c_bus; - struct psb_intel_i2c_chan *ddc_bus; + struct gma_i2c_chan *i2c_bus; }; @@ -90,7 +89,7 @@ static int psb_lvds_i2c_set_brightness(struct drm_device *dev, { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); - struct psb_intel_i2c_chan *lvds_i2c_bus = dev_priv->lvds_i2c_bus; + struct gma_i2c_chan *lvds_i2c_bus = dev_priv->lvds_i2c_bus; u8 out_buf[2]; unsigned int blc_i2c_brightness; @@ -113,7 +112,7 @@ static int psb_lvds_i2c_set_brightness(struct drm_device *dev, out_buf[0] = dev_priv->lvds_bl->brightnesscmd; out_buf[1] = (u8)blc_i2c_brightness; - if (i2c_transfer(&lvds_i2c_bus->adapter, msgs, 1) == 1) { + if (i2c_transfer(&lvds_i2c_bus->base, msgs, 1) == 1) { dev_dbg(dev->dev, "I2C set brightness.(command, value) (%d, %d)\n", dev_priv->lvds_bl->brightnesscmd, blc_i2c_brightness); @@ -492,12 +491,10 @@ static int psb_intel_lvds_get_modes(struct drm_connector *connector) struct drm_device *dev = connector->dev; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; - struct gma_encoder *gma_encoder = gma_attached_encoder(connector); - struct psb_intel_lvds_priv *lvds_priv = gma_encoder->dev_priv; int ret = 0; if (!IS_MRST(dev)) - ret = psb_intel_ddc_get_modes(connector, &lvds_priv->i2c_bus->adapter); + ret = psb_intel_ddc_get_modes(connector, connector->ddc); if (ret) return ret; @@ -512,20 +509,12 @@ static int psb_intel_lvds_get_modes(struct drm_connector *connector) return 0; } -/** - * psb_intel_lvds_destroy - unregister and free LVDS structures - * @connector: connector to free - * - * Unregister the DDC bus for this connector then free the driver private - * structure. - */ void psb_intel_lvds_destroy(struct drm_connector *connector) { struct gma_connector *gma_connector = to_gma_connector(connector); - struct gma_encoder *gma_encoder = gma_attached_encoder(connector); - struct psb_intel_lvds_priv *lvds_priv = gma_encoder->dev_priv; + struct gma_i2c_chan *ddc_bus = to_gma_i2c_chan(connector->ddc); - psb_intel_i2c_destroy(lvds_priv->ddc_bus); + gma_i2c_destroy(ddc_bus); drm_connector_cleanup(connector); kfree(gma_connector); } @@ -639,25 +628,28 @@ void psb_intel_lvds_init(struct drm_device *dev, struct drm_display_mode *scan; /* *modes, *bios_mode; */ struct drm_crtc *crtc; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); + struct gma_i2c_chan *ddc_bus; u32 lvds; int pipe; + int ret; gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL); if (!gma_encoder) { dev_err(dev->dev, "gma_encoder allocation error\n"); return; } + encoder = &gma_encoder->base; gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); if (!gma_connector) { dev_err(dev->dev, "gma_connector allocation error\n"); - goto failed_encoder; + goto err_free_encoder; } lvds_priv = kzalloc(sizeof(struct psb_intel_lvds_priv), GFP_KERNEL); if (!lvds_priv) { dev_err(dev->dev, "LVDS private allocation error\n"); - goto failed_connector; + goto err_free_connector; } gma_encoder->dev_priv = lvds_priv; @@ -666,12 +658,24 @@ void psb_intel_lvds_init(struct drm_device *dev, gma_connector->save = psb_intel_lvds_save; gma_connector->restore = psb_intel_lvds_restore; - encoder = &gma_encoder->base; - drm_connector_init(dev, connector, - &psb_intel_lvds_connector_funcs, - DRM_MODE_CONNECTOR_LVDS); + /* Set up the DDC bus. */ + ddc_bus = gma_i2c_create(dev, GPIOC, "LVDSDDC_C"); + if (!ddc_bus) { + dev_printk(KERN_ERR, dev->dev, + "DDC bus registration " "failed.\n"); + goto err_free_lvds_priv; + } - drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS); + ret = drm_connector_init_with_ddc(dev, connector, + &psb_intel_lvds_connector_funcs, + DRM_MODE_CONNECTOR_LVDS, + &ddc_bus->base); + if (ret) + goto err_ddc_destroy; + + ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS); + if (ret) + goto err_connector_cleanup; gma_connector_attach_encoder(gma_connector, gma_encoder); gma_encoder->type = INTEL_OUTPUT_LVDS; @@ -695,11 +699,11 @@ void psb_intel_lvds_init(struct drm_device *dev, * Set up I2C bus * FIXME: distroy i2c_bus when exit */ - lvds_priv->i2c_bus = psb_intel_i2c_create(dev, GPIOB, "LVDSBLC_B"); + lvds_priv->i2c_bus = gma_i2c_create(dev, GPIOB, "LVDSBLC_B"); if (!lvds_priv->i2c_bus) { dev_printk(KERN_ERR, dev->dev, "I2C bus registration failed.\n"); - goto failed_blc_i2c; + goto err_encoder_cleanup; } lvds_priv->i2c_bus->slave_addr = 0x2C; dev_priv->lvds_i2c_bus = lvds_priv->i2c_bus; @@ -714,20 +718,13 @@ void psb_intel_lvds_init(struct drm_device *dev, * if closed, act like it's not there for now */ - /* Set up the DDC bus. */ - lvds_priv->ddc_bus = psb_intel_i2c_create(dev, GPIOC, "LVDSDDC_C"); - if (!lvds_priv->ddc_bus) { - dev_printk(KERN_ERR, dev->dev, - "DDC bus registration " "failed.\n"); - goto failed_ddc; - } - /* * Attempt to get the fixed panel mode from DDC. Assume that the * preferred mode is the right one. */ mutex_lock(&dev->mode_config.mutex); - psb_intel_ddc_get_modes(connector, &lvds_priv->ddc_bus->adapter); + psb_intel_ddc_get_modes(connector, &ddc_bus->base); + list_for_each_entry(scan, &connector->probed_modes, head) { if (scan->type & DRM_MODE_TYPE_PREFERRED) { mode_dev->panel_fixed_mode = @@ -773,7 +770,7 @@ void psb_intel_lvds_init(struct drm_device *dev, /* If we still don't have a mode after all that, give up. */ if (!mode_dev->panel_fixed_mode) { dev_err(dev->dev, "Found no modes on the lvds, ignoring the LVDS\n"); - goto failed_find; + goto err_unlock; } /* @@ -784,17 +781,20 @@ out: mutex_unlock(&dev->mode_config.mutex); return; -failed_find: +err_unlock: mutex_unlock(&dev->mode_config.mutex); - psb_intel_i2c_destroy(lvds_priv->ddc_bus); -failed_ddc: - psb_intel_i2c_destroy(lvds_priv->i2c_bus); -failed_blc_i2c: + gma_i2c_destroy(lvds_priv->i2c_bus); +err_encoder_cleanup: drm_encoder_cleanup(encoder); +err_connector_cleanup: drm_connector_cleanup(connector); -failed_connector: +err_ddc_destroy: + gma_i2c_destroy(ddc_bus); +err_free_lvds_priv: + kfree(lvds_priv); +err_free_connector: kfree(gma_connector); -failed_encoder: +err_free_encoder: kfree(gma_encoder); } diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c index 4a8941fa0815..6d11e7938c83 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c @@ -69,56 +69,7 @@ static struct pci_driver hyperv_pci_driver = { .remove = hyperv_pci_remove, }; -static int hyperv_setup_gen1(struct hyperv_drm_device *hv) -{ - struct drm_device *dev = &hv->dev; - struct pci_dev *pdev; - int ret; - - pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT, - PCI_DEVICE_ID_HYPERV_VIDEO, NULL); - if (!pdev) { - drm_err(dev, "Unable to find PCI Hyper-V video\n"); - return -ENODEV; - } - - ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, &hyperv_driver); - if (ret) { - drm_err(dev, "Not able to remove boot fb\n"); - return ret; - } - - if (pci_request_region(pdev, 0, DRIVER_NAME) != 0) - drm_warn(dev, "Cannot request framebuffer, boot fb still active?\n"); - - if ((pdev->resource[0].flags & IORESOURCE_MEM) == 0) { - drm_err(dev, "Resource at bar 0 is not IORESOURCE_MEM\n"); - ret = -ENODEV; - goto error; - } - - hv->fb_base = pci_resource_start(pdev, 0); - hv->fb_size = pci_resource_len(pdev, 0); - if (!hv->fb_base) { - drm_err(dev, "Resource not available\n"); - ret = -ENODEV; - goto error; - } - - hv->fb_size = min(hv->fb_size, - (unsigned long)(hv->mmio_megabytes * 1024 * 1024)); - hv->vram = devm_ioremap(&pdev->dev, hv->fb_base, hv->fb_size); - if (!hv->vram) { - drm_err(dev, "Failed to map vram\n"); - ret = -ENOMEM; - } - -error: - pci_dev_put(pdev); - return ret; -} - -static int hyperv_setup_gen2(struct hyperv_drm_device *hv, +static int hyperv_setup_vram(struct hyperv_drm_device *hv, struct hv_device *hdev) { struct drm_device *dev = &hv->dev; @@ -181,10 +132,7 @@ static int hyperv_vmbus_probe(struct hv_device *hdev, goto err_hv_set_drv_data; } - if (efi_enabled(EFI_BOOT)) - ret = hyperv_setup_gen2(hv, hdev); - else - ret = hyperv_setup_gen1(hv); + ret = hyperv_setup_vram(hv, hdev); if (ret) goto err_vmbus_close; @@ -225,29 +173,13 @@ static int hyperv_vmbus_remove(struct hv_device *hdev) { struct drm_device *dev = hv_get_drvdata(hdev); struct hyperv_drm_device *hv = to_hv(dev); - struct pci_dev *pdev; drm_dev_unplug(dev); drm_atomic_helper_shutdown(dev); vmbus_close(hdev->channel); hv_set_drvdata(hdev, NULL); - /* - * Free allocated MMIO memory only on Gen2 VMs. - * On Gen1 VMs, release the PCI device - */ - if (efi_enabled(EFI_BOOT)) { - vmbus_free_mmio(hv->mem->start, hv->fb_size); - } else { - pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT, - PCI_DEVICE_ID_HYPERV_VIDEO, NULL); - if (!pdev) { - drm_err(dev, "Unable to find PCI Hyper-V video\n"); - return -ENODEV; - } - pci_release_region(pdev, 0); - pci_dev_put(pdev); - } + vmbus_free_mmio(hv->mem->start, hv->fb_size); return 0; } diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c index 452d773fd4e3..6c3954479047 100644 --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c @@ -590,6 +590,8 @@ static void intel_connector_info(struct seq_file *m, seq_puts(m, "\tHDCP version: "); intel_hdcp_info(m, intel_connector); + seq_printf(m, "\tmax bpc: %u\n", connector->display_info.bpc); + intel_panel_info(m, intel_connector); seq_printf(m, "\tmodes:\n"); @@ -2202,6 +2204,29 @@ static const struct file_operations i915_dsc_bpp_fops = { .write = i915_dsc_bpp_write }; +/* + * Returns the Current CRTC's bpc. + * Example usage: cat /sys/kernel/debug/dri/0/crtc-0/i915_current_bpc + */ +static int i915_current_bpc_show(struct seq_file *m, void *data) +{ + struct intel_crtc *crtc = to_intel_crtc(m->private); + struct intel_crtc_state *crtc_state; + int ret; + + ret = drm_modeset_lock_single_interruptible(&crtc->base.mutex); + if (ret) + return ret; + + crtc_state = to_intel_crtc_state(crtc->base.state); + seq_printf(m, "Current: %u\n", crtc_state->pipe_bpp / 3); + + drm_modeset_unlock(&crtc->base.mutex); + + return ret; +} +DEFINE_SHOW_ATTRIBUTE(i915_current_bpc); + /** * intel_connector_debugfs_add - add i915 specific connector debugfs files * @connector: pointer to a registered drm_connector @@ -2272,4 +2297,7 @@ void intel_crtc_debugfs_add(struct drm_crtc *crtc) crtc_updates_add(crtc); intel_fbc_crtc_debugfs_add(to_intel_crtc(crtc)); + + debugfs_create_file("i915_current_bpc", 0444, crtc->debugfs_entry, crtc, + &i915_current_bpc_fops); } diff --git a/drivers/gpu/drm/meson/meson_encoder_cvbs.c b/drivers/gpu/drm/meson/meson_encoder_cvbs.c index fd8db97ba8ba..8110a6e39320 100644 --- a/drivers/gpu/drm/meson/meson_encoder_cvbs.c +++ b/drivers/gpu/drm/meson/meson_encoder_cvbs.c @@ -238,6 +238,7 @@ int meson_encoder_cvbs_init(struct meson_drm *priv) } meson_encoder_cvbs->next_bridge = of_drm_find_bridge(remote); + of_node_put(remote); if (!meson_encoder_cvbs->next_bridge) { dev_err(priv->dev, "Failed to find CVBS Connector bridge\n"); return -EPROBE_DEFER; diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.c b/drivers/gpu/drm/meson/meson_encoder_hdmi.c index 5e306de6f485..2f616c55c271 100644 --- a/drivers/gpu/drm/meson/meson_encoder_hdmi.c +++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.c @@ -218,7 +218,8 @@ static void meson_encoder_hdmi_atomic_enable(struct drm_bridge *bridge, if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) { ycrcb_map = VPU_HDMI_OUTPUT_CRYCB; yuv420_mode = true; - } + } else if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYVY8_1X16) + ycrcb_map = VPU_HDMI_OUTPUT_CRYCB; /* VENC + VENC-DVI Mode setup */ meson_venc_hdmi_mode_set(priv, vic, ycrcb_map, yuv420_mode, mode); @@ -230,6 +231,10 @@ static void meson_encoder_hdmi_atomic_enable(struct drm_bridge *bridge, /* Setup YUV420 to HDMI-TX, no 10bit diphering */ writel_relaxed(2 | (2 << 2), priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); + else if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYVY8_1X16) + /* Setup YUV422 to HDMI-TX, no 10bit diphering */ + writel_relaxed(1 | (2 << 2), + priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); else /* Setup YUV444 to HDMI-TX, no 10bit diphering */ writel_relaxed(0, priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); @@ -257,6 +262,7 @@ static void meson_encoder_hdmi_atomic_disable(struct drm_bridge *bridge, static const u32 meson_encoder_hdmi_out_bus_fmts[] = { MEDIA_BUS_FMT_YUV8_1X24, + MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_UYYVYY8_0_5X24, }; @@ -365,7 +371,8 @@ int meson_encoder_hdmi_init(struct meson_drm *priv) meson_encoder_hdmi->next_bridge = of_drm_find_bridge(remote); if (!meson_encoder_hdmi->next_bridge) { dev_err(priv->dev, "Failed to find HDMI transceiver bridge\n"); - return -EPROBE_DEFER; + ret = -EPROBE_DEFER; + goto err_put_node; } /* HDMI Encoder Bridge */ @@ -383,7 +390,7 @@ int meson_encoder_hdmi_init(struct meson_drm *priv) DRM_MODE_ENCODER_TMDS); if (ret) { dev_err(priv->dev, "Failed to init HDMI encoder: %d\n", ret); - return ret; + goto err_put_node; } meson_encoder_hdmi->encoder.possible_crtcs = BIT(0); @@ -393,7 +400,7 @@ int meson_encoder_hdmi_init(struct meson_drm *priv) DRM_BRIDGE_ATTACH_NO_CONNECTOR); if (ret) { dev_err(priv->dev, "Failed to attach bridge: %d\n", ret); - return ret; + goto err_put_node; } /* Initialize & attach Bridge Connector */ @@ -401,7 +408,8 @@ int meson_encoder_hdmi_init(struct meson_drm *priv) &meson_encoder_hdmi->encoder); if (IS_ERR(meson_encoder_hdmi->connector)) { dev_err(priv->dev, "Unable to create HDMI bridge connector\n"); - return PTR_ERR(meson_encoder_hdmi->connector); + ret = PTR_ERR(meson_encoder_hdmi->connector); + goto err_put_node; } drm_connector_attach_encoder(meson_encoder_hdmi->connector, &meson_encoder_hdmi->encoder); @@ -428,6 +436,7 @@ int meson_encoder_hdmi_init(struct meson_drm *priv) meson_encoder_hdmi->connector->ycbcr_420_allowed = true; pdev = of_find_device_by_node(remote); + of_node_put(remote); if (pdev) { struct cec_connector_info conn_info; struct cec_notifier *notifier; @@ -435,8 +444,10 @@ int meson_encoder_hdmi_init(struct meson_drm *priv) cec_fill_conn_info_from_drm(&conn_info, meson_encoder_hdmi->connector); notifier = cec_notifier_conn_register(&pdev->dev, NULL, &conn_info); - if (!notifier) + if (!notifier) { + put_device(&pdev->dev); return -ENOMEM; + } meson_encoder_hdmi->cec_notifier = notifier; } @@ -444,4 +455,8 @@ int meson_encoder_hdmi_init(struct meson_drm *priv) dev_dbg(priv->dev, "HDMI encoder initialized\n"); return 0; + +err_put_node: + of_node_put(remote); + return ret; } diff --git a/drivers/gpu/drm/mgag200/Makefile b/drivers/gpu/drm/mgag200/Makefile index d4f766522483..89558549c3af 100644 --- a/drivers/gpu/drm/mgag200/Makefile +++ b/drivers/gpu/drm/mgag200/Makefile @@ -1,4 +1,16 @@ # SPDX-License-Identifier: GPL-2.0-only -mgag200-y := mgag200_drv.o mgag200_i2c.o mgag200_mm.o mgag200_mode.o mgag200_pll.o +mgag200-y := \ + mgag200_drv.o \ + mgag200_g200.o \ + mgag200_g200eh.o \ + mgag200_g200eh3.o \ + mgag200_g200er.o \ + mgag200_g200ev.o \ + mgag200_g200ew3.o \ + mgag200_g200se.o \ + mgag200_g200wb.o \ + mgag200_i2c.o \ + mgag200_mode.o \ + mgag200_pll.o obj-$(CONFIG_DRM_MGAG200) += mgag200.o diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 08839460606f..361eb7dffda1 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -24,6 +24,71 @@ int mgag200_modeset = -1; MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); module_param_named(modeset, mgag200_modeset, int, 0400); +int mgag200_init_pci_options(struct pci_dev *pdev, u32 option, u32 option2) +{ + struct device *dev = &pdev->dev; + int err; + + err = pci_read_config_dword(pdev, PCI_MGA_OPTION, &option); + if (err != PCIBIOS_SUCCESSFUL) { + dev_err(dev, "pci_read_config_dword(PCI_MGA_OPTION) failed: %d\n", err); + return pcibios_err_to_errno(err); + } + + err = pci_write_config_dword(pdev, PCI_MGA_OPTION, option); + if (err != PCIBIOS_SUCCESSFUL) { + dev_err(dev, "pci_write_config_dword(PCI_MGA_OPTION) failed: %d\n", err); + return pcibios_err_to_errno(err); + } + + err = pci_write_config_dword(pdev, PCI_MGA_OPTION2, option2); + if (err != PCIBIOS_SUCCESSFUL) { + dev_err(dev, "pci_write_config_dword(PCI_MGA_OPTION2) failed: %d\n", err); + return pcibios_err_to_errno(err); + } + + return 0; +} + +resource_size_t mgag200_probe_vram(void __iomem *mem, resource_size_t size) +{ + int offset; + int orig; + int test1, test2; + int orig1, orig2; + size_t vram_size; + + /* Probe */ + orig = ioread16(mem); + iowrite16(0, mem); + + vram_size = size; + + for (offset = 0x100000; offset < vram_size; offset += 0x4000) { + orig1 = ioread8(mem + offset); + orig2 = ioread8(mem + offset + 0x100); + + iowrite16(0xaa55, mem + offset); + iowrite16(0xaa55, mem + offset + 0x100); + + test1 = ioread16(mem + offset); + test2 = ioread16(mem); + + iowrite16(orig1, mem + offset); + iowrite16(orig2, mem + offset + 0x100); + + if (test1 != 0xaa55) + break; + + if (test2) + break; + } + + iowrite16(orig, mem); + + return offset - 65536; +} + /* * DRM driver */ @@ -46,263 +111,87 @@ static const struct drm_driver mgag200_driver = { * DRM device */ -static bool mgag200_has_sgram(struct mga_device *mdev) +resource_size_t mgag200_device_probe_vram(struct mga_device *mdev) { - struct drm_device *dev = &mdev->base; - struct pci_dev *pdev = to_pci_dev(dev->dev); - u32 option; - int ret; - - ret = pci_read_config_dword(pdev, PCI_MGA_OPTION, &option); - if (drm_WARN(dev, ret, "failed to read PCI config dword: %d\n", ret)) - return false; - - return !!(option & PCI_MGA_OPTION_HARDPWMSK); + return mgag200_probe_vram(mdev->vram, resource_size(mdev->vram_res)); } -static int mgag200_regs_init(struct mga_device *mdev) +int mgag200_device_preinit(struct mga_device *mdev) { struct drm_device *dev = &mdev->base; struct pci_dev *pdev = to_pci_dev(dev->dev); - u32 option, option2; - u8 crtcext3; - int ret; - - ret = drmm_mutex_init(dev, &mdev->rmmio_lock); - if (ret) - return ret; - - switch (mdev->type) { - case G200_PCI: - case G200_AGP: - if (mgag200_has_sgram(mdev)) - option = 0x4049cd21; - else - option = 0x40499121; - option2 = 0x00008000; - break; - case G200_SE_A: - case G200_SE_B: - option = 0x40049120; - if (mgag200_has_sgram(mdev)) - option |= PCI_MGA_OPTION_HARDPWMSK; - option2 = 0x00008000; - break; - case G200_WB: - case G200_EW3: - option = 0x41049120; - option2 = 0x0000b000; - break; - case G200_EV: - option = 0x00000120; - option2 = 0x0000b000; - break; - case G200_EH: - case G200_EH3: - option = 0x00000120; - option2 = 0x0000b000; - break; - default: - option = 0; - option2 = 0; - } - - if (option) - pci_write_config_dword(pdev, PCI_MGA_OPTION, option); - if (option2) - pci_write_config_dword(pdev, PCI_MGA_OPTION2, option2); + resource_size_t start, len; + struct resource *res; /* BAR 1 contains registers */ - mdev->rmmio_base = pci_resource_start(pdev, 1); - mdev->rmmio_size = pci_resource_len(pdev, 1); - if (!devm_request_mem_region(dev->dev, mdev->rmmio_base, - mdev->rmmio_size, "mgadrmfb_mmio")) { - drm_err(dev, "can't reserve mmio registers\n"); - return -ENOMEM; + start = pci_resource_start(pdev, 1); + len = pci_resource_len(pdev, 1); + + res = devm_request_mem_region(dev->dev, start, len, "mgadrmfb_mmio"); + if (!res) { + drm_err(dev, "devm_request_mem_region(MMIO) failed\n"); + return -ENXIO; } + mdev->rmmio_res = res; mdev->rmmio = pcim_iomap(pdev, 1, 0); - if (mdev->rmmio == NULL) + if (!mdev->rmmio) return -ENOMEM; - RREG_ECRT(0x03, crtcext3); - crtcext3 |= MGAREG_CRTCEXT3_MGAMODE; - WREG_ECRT(0x03, crtcext3); - - return 0; -} - -static void mgag200_g200_interpret_bios(struct mga_device *mdev, - const unsigned char *bios, - size_t size) -{ - static const char matrox[] = {'M', 'A', 'T', 'R', 'O', 'X'}; - static const unsigned int expected_length[6] = { - 0, 64, 64, 64, 128, 128 - }; - struct drm_device *dev = &mdev->base; - const unsigned char *pins; - unsigned int pins_len, version; - int offset; - int tmp; - - /* Test for MATROX string. */ - if (size < 45 + sizeof(matrox)) - return; - if (memcmp(&bios[45], matrox, sizeof(matrox)) != 0) - return; - - /* Get the PInS offset. */ - if (size < MGA_BIOS_OFFSET + 2) - return; - offset = (bios[MGA_BIOS_OFFSET + 1] << 8) | bios[MGA_BIOS_OFFSET]; - - /* Get PInS data structure. */ - - if (size < offset + 6) - return; - pins = bios + offset; - if (pins[0] == 0x2e && pins[1] == 0x41) { - version = pins[5]; - pins_len = pins[2]; - } else { - version = 1; - pins_len = pins[0] + (pins[1] << 8); - } - - if (version < 1 || version > 5) { - drm_warn(dev, "Unknown BIOS PInS version: %d\n", version); - return; - } - if (pins_len != expected_length[version]) { - drm_warn(dev, "Unexpected BIOS PInS size: %d expected: %d\n", - pins_len, expected_length[version]); - return; - } - if (size < offset + pins_len) - return; + /* BAR 0 is VRAM */ - drm_dbg_kms(dev, "MATROX BIOS PInS version %d size: %d found\n", - version, pins_len); + start = pci_resource_start(pdev, 0); + len = pci_resource_len(pdev, 0); - /* Extract the clock values */ - - switch (version) { - case 1: - tmp = pins[24] + (pins[25] << 8); - if (tmp) - mdev->model.g200.pclk_max = tmp * 10; - break; - case 2: - if (pins[41] != 0xff) - mdev->model.g200.pclk_max = (pins[41] + 100) * 1000; - break; - case 3: - if (pins[36] != 0xff) - mdev->model.g200.pclk_max = (pins[36] + 100) * 1000; - if (pins[52] & 0x20) - mdev->model.g200.ref_clk = 14318; - break; - case 4: - if (pins[39] != 0xff) - mdev->model.g200.pclk_max = pins[39] * 4 * 1000; - if (pins[92] & 0x01) - mdev->model.g200.ref_clk = 14318; - break; - case 5: - tmp = pins[4] ? 8000 : 6000; - if (pins[123] != 0xff) - mdev->model.g200.pclk_min = pins[123] * tmp; - if (pins[38] != 0xff) - mdev->model.g200.pclk_max = pins[38] * tmp; - if (pins[110] & 0x01) - mdev->model.g200.ref_clk = 14318; - break; - default: - break; + res = devm_request_mem_region(dev->dev, start, len, "mgadrmfb_vram"); + if (!res) { + drm_err(dev, "devm_request_mem_region(VRAM) failed\n"); + return -ENXIO; } -} - -static void mgag200_g200_init_refclk(struct mga_device *mdev) -{ - struct drm_device *dev = &mdev->base; - struct pci_dev *pdev = to_pci_dev(dev->dev); - unsigned char __iomem *rom; - unsigned char *bios; - size_t size; - - mdev->model.g200.pclk_min = 50000; - mdev->model.g200.pclk_max = 230000; - mdev->model.g200.ref_clk = 27050; + mdev->vram_res = res; - rom = pci_map_rom(pdev, &size); - if (!rom) - return; + /* Don't fail on errors, but performance might be reduced. */ + devm_arch_io_reserve_memtype_wc(dev->dev, res->start, resource_size(res)); + devm_arch_phys_wc_add(dev->dev, res->start, resource_size(res)); - bios = vmalloc(size); - if (!bios) - goto out; - memcpy_fromio(bios, rom, size); - - if (size != 0 && bios[0] == 0x55 && bios[1] == 0xaa) - mgag200_g200_interpret_bios(mdev, bios, size); - - drm_dbg_kms(dev, "pclk_min: %ld pclk_max: %ld ref_clk: %ld\n", - mdev->model.g200.pclk_min, mdev->model.g200.pclk_max, - mdev->model.g200.ref_clk); + mdev->vram = devm_ioremap(dev->dev, res->start, resource_size(res)); + if (!mdev->vram) + return -ENOMEM; - vfree(bios); -out: - pci_unmap_rom(pdev, rom); + return 0; } -static void mgag200_g200se_init_unique_id(struct mga_device *mdev) +int mgag200_device_init(struct mga_device *mdev, enum mga_type type, + const struct mgag200_device_info *info) { struct drm_device *dev = &mdev->base; - - /* stash G200 SE model number for later use */ - mdev->model.g200se.unique_rev_id = RREG32(0x1e24); - - drm_dbg(dev, "G200 SE unique revision id is 0x%x\n", - mdev->model.g200se.unique_rev_id); -} - -static struct mga_device * -mgag200_device_create(struct pci_dev *pdev, enum mga_type type, unsigned long flags) -{ - struct mga_device *mdev; - struct drm_device *dev; + u8 crtcext3, misc; int ret; - mdev = devm_drm_dev_alloc(&pdev->dev, &mgag200_driver, struct mga_device, base); - if (IS_ERR(mdev)) - return mdev; - dev = &mdev->base; - - pci_set_drvdata(pdev, dev); - - mdev->flags = flags; + mdev->info = info; mdev->type = type; - ret = mgag200_regs_init(mdev); + ret = drmm_mutex_init(dev, &mdev->rmmio_lock); if (ret) - return ERR_PTR(ret); + return ret; - if (mdev->type == G200_PCI || mdev->type == G200_AGP) - mgag200_g200_init_refclk(mdev); - else if (IS_G200_SE(mdev)) - mgag200_g200se_init_unique_id(mdev); + mutex_lock(&mdev->rmmio_lock); - ret = mgag200_mm_init(mdev); - if (ret) - return ERR_PTR(ret); + RREG_ECRT(0x03, crtcext3); + crtcext3 |= MGAREG_CRTCEXT3_MGAMODE; + WREG_ECRT(0x03, crtcext3); - ret = mgag200_modeset_init(mdev); - if (ret) - return ERR_PTR(ret); + WREG_ECRT(0x04, 0x00); - return mdev; + misc = RREG8(MGA_MISC_IN); + misc |= MGAREG_MISC_RAMMAPEN | + MGAREG_MISC_HIGH_PG_SEL; + WREG8(MGA_MISC_OUT, misc); + + mutex_unlock(&mdev->rmmio_lock); + + return 0; } /* @@ -312,8 +201,7 @@ mgag200_device_create(struct pci_dev *pdev, enum mga_type type, unsigned long fl static const struct pci_device_id mgag200_pciidlist[] = { { PCI_VENDOR_ID_MATROX, 0x520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_PCI }, { PCI_VENDOR_ID_MATROX, 0x521, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_AGP }, - { PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - G200_SE_A | MGAG200_FLAG_HW_BUG_NO_STARTADD}, + { PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_A }, { PCI_VENDOR_ID_MATROX, 0x524, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_B }, { PCI_VENDOR_ID_MATROX, 0x530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EV }, { PCI_VENDOR_ID_MATROX, 0x532, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_WB }, @@ -326,22 +214,10 @@ static const struct pci_device_id mgag200_pciidlist[] = { MODULE_DEVICE_TABLE(pci, mgag200_pciidlist); -static enum mga_type mgag200_type_from_driver_data(kernel_ulong_t driver_data) -{ - return (enum mga_type)(driver_data & MGAG200_TYPE_MASK); -} - -static unsigned long mgag200_flags_from_driver_data(kernel_ulong_t driver_data) -{ - return driver_data & MGAG200_FLAG_MASK; -} - static int mgag200_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { - kernel_ulong_t driver_data = ent->driver_data; - enum mga_type type = mgag200_type_from_driver_data(driver_data); - unsigned long flags = mgag200_flags_from_driver_data(driver_data); + enum mga_type type = (enum mga_type)ent->driver_data; struct mga_device *mdev; struct drm_device *dev; int ret; @@ -354,7 +230,37 @@ mgag200_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ret) return ret; - mdev = mgag200_device_create(pdev, type, flags); + switch (type) { + case G200_PCI: + case G200_AGP: + mdev = mgag200_g200_device_create(pdev, &mgag200_driver, type); + break; + case G200_SE_A: + case G200_SE_B: + mdev = mgag200_g200se_device_create(pdev, &mgag200_driver, type); + break; + case G200_WB: + mdev = mgag200_g200wb_device_create(pdev, &mgag200_driver, type); + break; + case G200_EV: + mdev = mgag200_g200ev_device_create(pdev, &mgag200_driver, type); + break; + case G200_EH: + mdev = mgag200_g200eh_device_create(pdev, &mgag200_driver, type); + break; + case G200_EH3: + mdev = mgag200_g200eh3_device_create(pdev, &mgag200_driver, type); + break; + case G200_ER: + mdev = mgag200_g200er_device_create(pdev, &mgag200_driver, type); + break; + case G200_EW3: + mdev = mgag200_g200ew3_device_create(pdev, &mgag200_driver, type); + break; + default: + dev_err(&pdev->dev, "Device type %d is unsupported\n", type); + return -ENODEV; + } if (IS_ERR(mdev)) return PTR_ERR(mdev); dev = &mdev->base; diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h index a18384c41fc4..301c4ab46539 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.h +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -168,8 +168,6 @@ static inline struct mgag200_crtc_state *to_mgag200_crtc_state(struct drm_crtc_s return container_of(base, struct mgag200_crtc_state, base); } -#define to_mga_connector(x) container_of(x, struct mga_connector, base) - struct mga_i2c_chan { struct i2c_adapter adapter; struct drm_device *dev; @@ -177,17 +175,6 @@ struct mga_i2c_chan { int data, clock; }; -struct mga_connector { - struct drm_connector base; - struct mga_i2c_chan *i2c; -}; - -struct mga_mc { - resource_size_t vram_size; - resource_size_t vram_base; - resource_size_t vram_window; -}; - enum mga_type { G200_PCI, G200_AGP, @@ -201,44 +188,66 @@ enum mga_type { G200_EW3, }; -/* HW does not handle 'startadd' field correct. */ -#define MGAG200_FLAG_HW_BUG_NO_STARTADD (1ul << 8) +#define IS_G200_SE(mdev) (mdev->type == G200_SE_A || mdev->type == G200_SE_B) -#define MGAG200_TYPE_MASK (0x000000ff) -#define MGAG200_FLAG_MASK (0x00ffff00) +struct mgag200_device_info { + u16 max_hdisplay; + u16 max_vdisplay; + + /* + * Maximum memory bandwidth (MiB/sec). Setting this to zero disables + * the rsp test during mode validation. + */ + unsigned long max_mem_bandwidth; + + /* HW has external source (e.g., BMC) to synchronize with */ + bool has_vidrst:1; + + struct { + unsigned data_bit:3; + unsigned clock_bit:3; + } i2c; + + /* + * HW does not handle 'startadd' register correctly. Always set + * it's value to 0. + */ + bool bug_no_startadd:1; +}; -#define IS_G200_SE(mdev) (mdev->type == G200_SE_A || mdev->type == G200_SE_B) +#define MGAG200_DEVICE_INFO_INIT(_max_hdisplay, _max_vdisplay, _max_mem_bandwidth, \ + _has_vidrst, _i2c_data_bit, _i2c_clock_bit, \ + _bug_no_startadd) \ + { \ + .max_hdisplay = (_max_hdisplay), \ + .max_vdisplay = (_max_vdisplay), \ + .max_mem_bandwidth = (_max_mem_bandwidth), \ + .has_vidrst = (_has_vidrst), \ + .i2c = { \ + .data_bit = (_i2c_data_bit), \ + .clock_bit = (_i2c_clock_bit), \ + }, \ + .bug_no_startadd = (_bug_no_startadd), \ + } struct mga_device { - struct drm_device base; - unsigned long flags; + struct drm_device base; - struct mutex rmmio_lock; /* Protects access to rmmio */ - resource_size_t rmmio_base; - resource_size_t rmmio_size; - void __iomem *rmmio; + const struct mgag200_device_info *info; - struct mga_mc mc; + struct resource *rmmio_res; + void __iomem *rmmio; + struct mutex rmmio_lock; /* Protects access to rmmio */ + struct resource *vram_res; void __iomem *vram; - size_t vram_fb_available; + resource_size_t vram_available; enum mga_type type; - union { - struct { - long ref_clk; - long pclk_min; - long pclk_max; - } g200; - struct { - /* SE model number stored in reg 0x1e24 */ - u32 unique_rev_id; - } g200se; - } model; - - struct mga_connector connector; struct mgag200_pll pixpll; + struct mga_i2c_chan i2c; + struct drm_connector connector; struct drm_simple_display_pipe display_pipe; }; @@ -247,15 +256,64 @@ static inline struct mga_device *to_mga_device(struct drm_device *dev) return container_of(dev, struct mga_device, base); } +struct mgag200_g200_device { + struct mga_device base; + + /* PLL constants */ + long ref_clk; + long pclk_min; + long pclk_max; +}; + +static inline struct mgag200_g200_device *to_mgag200_g200_device(struct drm_device *dev) +{ + return container_of(to_mga_device(dev), struct mgag200_g200_device, base); +} + +struct mgag200_g200se_device { + struct mga_device base; + + /* SE model number stored in reg 0x1e24 */ + u32 unique_rev_id; +}; + +static inline struct mgag200_g200se_device *to_mgag200_g200se_device(struct drm_device *dev) +{ + return container_of(to_mga_device(dev), struct mgag200_g200se_device, base); +} + + /* mgag200_drv.c */ +int mgag200_init_pci_options(struct pci_dev *pdev, u32 option, u32 option2); +resource_size_t mgag200_probe_vram(void __iomem *mem, resource_size_t size); +resource_size_t mgag200_device_probe_vram(struct mga_device *mdev); +int mgag200_device_preinit(struct mga_device *mdev); +int mgag200_device_init(struct mga_device *mdev, enum mga_type type, + const struct mgag200_device_info *info); + + /* mgag200_<device type>.c */ +struct mga_device *mgag200_g200_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type); +struct mga_device *mgag200_g200se_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type); +struct mga_device *mgag200_g200wb_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type); +struct mga_device *mgag200_g200ev_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type); +struct mga_device *mgag200_g200eh_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type); +struct mga_device *mgag200_g200eh3_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type); +struct mga_device *mgag200_g200er_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type); +struct mga_device *mgag200_g200ew3_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type); + /* mgag200_mode.c */ -int mgag200_modeset_init(struct mga_device *mdev); +resource_size_t mgag200_device_probe_vram(struct mga_device *mdev); +int mgag200_modeset_init(struct mga_device *mdev, resource_size_t vram_fb_available); /* mgag200_i2c.c */ -struct mga_i2c_chan *mgag200_i2c_create(struct drm_device *dev); -void mgag200_i2c_destroy(struct mga_i2c_chan *i2c); - - /* mgag200_mm.c */ -int mgag200_mm_init(struct mga_device *mdev); +int mgag200_i2c_init(struct mga_device *mdev, struct mga_i2c_chan *i2c); /* mgag200_pll.c */ int mgag200_pixpll_init(struct mgag200_pll *pixpll, struct mga_device *mdev); diff --git a/drivers/gpu/drm/mgag200/mgag200_g200.c b/drivers/gpu/drm/mgag200/mgag200_g200.c new file mode 100644 index 000000000000..616e11391e02 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_g200.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "mgag200_drv.h" + +static int mgag200_g200_init_pci_options(struct pci_dev *pdev) +{ + struct device *dev = &pdev->dev; + bool has_sgram; + u32 option; + int err; + + err = pci_read_config_dword(pdev, PCI_MGA_OPTION, &option); + if (err != PCIBIOS_SUCCESSFUL) { + dev_err(dev, "pci_read_config_dword(PCI_MGA_OPTION) failed: %d\n", err); + return pcibios_err_to_errno(err); + } + + has_sgram = !!(option & PCI_MGA_OPTION_HARDPWMSK); + + if (has_sgram) + option = 0x4049cd21; + else + option = 0x40499121; + + return mgag200_init_pci_options(pdev, option, 0x00008000); +} + +/* + * DRM Device + */ + +static const struct mgag200_device_info mgag200_g200_device_info = + MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, false, 1, 3, false); + +static void mgag200_g200_interpret_bios(struct mgag200_g200_device *g200, + const unsigned char *bios, size_t size) +{ + static const char matrox[] = {'M', 'A', 'T', 'R', 'O', 'X'}; + static const unsigned int expected_length[6] = { + 0, 64, 64, 64, 128, 128 + }; + struct mga_device *mdev = &g200->base; + struct drm_device *dev = &mdev->base; + const unsigned char *pins; + unsigned int pins_len, version; + int offset; + int tmp; + + /* Test for MATROX string. */ + if (size < 45 + sizeof(matrox)) + return; + if (memcmp(&bios[45], matrox, sizeof(matrox)) != 0) + return; + + /* Get the PInS offset. */ + if (size < MGA_BIOS_OFFSET + 2) + return; + offset = (bios[MGA_BIOS_OFFSET + 1] << 8) | bios[MGA_BIOS_OFFSET]; + + /* Get PInS data structure. */ + + if (size < offset + 6) + return; + pins = bios + offset; + if (pins[0] == 0x2e && pins[1] == 0x41) { + version = pins[5]; + pins_len = pins[2]; + } else { + version = 1; + pins_len = pins[0] + (pins[1] << 8); + } + + if (version < 1 || version > 5) { + drm_warn(dev, "Unknown BIOS PInS version: %d\n", version); + return; + } + if (pins_len != expected_length[version]) { + drm_warn(dev, "Unexpected BIOS PInS size: %d expected: %d\n", + pins_len, expected_length[version]); + return; + } + if (size < offset + pins_len) + return; + + drm_dbg_kms(dev, "MATROX BIOS PInS version %d size: %d found\n", version, pins_len); + + /* Extract the clock values */ + + switch (version) { + case 1: + tmp = pins[24] + (pins[25] << 8); + if (tmp) + g200->pclk_max = tmp * 10; + break; + case 2: + if (pins[41] != 0xff) + g200->pclk_max = (pins[41] + 100) * 1000; + break; + case 3: + if (pins[36] != 0xff) + g200->pclk_max = (pins[36] + 100) * 1000; + if (pins[52] & 0x20) + g200->ref_clk = 14318; + break; + case 4: + if (pins[39] != 0xff) + g200->pclk_max = pins[39] * 4 * 1000; + if (pins[92] & 0x01) + g200->ref_clk = 14318; + break; + case 5: + tmp = pins[4] ? 8000 : 6000; + if (pins[123] != 0xff) + g200->pclk_min = pins[123] * tmp; + if (pins[38] != 0xff) + g200->pclk_max = pins[38] * tmp; + if (pins[110] & 0x01) + g200->ref_clk = 14318; + break; + default: + break; + } +} + +static void mgag200_g200_init_refclk(struct mgag200_g200_device *g200) +{ + struct mga_device *mdev = &g200->base; + struct drm_device *dev = &mdev->base; + struct pci_dev *pdev = to_pci_dev(dev->dev); + unsigned char __iomem *rom; + unsigned char *bios; + size_t size; + + g200->pclk_min = 50000; + g200->pclk_max = 230000; + g200->ref_clk = 27050; + + rom = pci_map_rom(pdev, &size); + if (!rom) + return; + + bios = vmalloc(size); + if (!bios) + goto out; + memcpy_fromio(bios, rom, size); + + if (size != 0 && bios[0] == 0x55 && bios[1] == 0xaa) + mgag200_g200_interpret_bios(g200, bios, size); + + drm_dbg_kms(dev, "pclk_min: %ld pclk_max: %ld ref_clk: %ld\n", + g200->pclk_min, g200->pclk_max, g200->ref_clk); + + vfree(bios); +out: + pci_unmap_rom(pdev, rom); +} + +struct mga_device *mgag200_g200_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type) +{ + struct mgag200_g200_device *g200; + struct mga_device *mdev; + struct drm_device *dev; + resource_size_t vram_available; + int ret; + + g200 = devm_drm_dev_alloc(&pdev->dev, drv, struct mgag200_g200_device, base.base); + if (IS_ERR(g200)) + return ERR_CAST(g200); + mdev = &g200->base; + dev = &mdev->base; + + pci_set_drvdata(pdev, dev); + + ret = mgag200_g200_init_pci_options(pdev); + if (ret) + return ERR_PTR(ret); + + ret = mgag200_device_preinit(mdev); + if (ret) + return ERR_PTR(ret); + + mgag200_g200_init_refclk(g200); + + ret = mgag200_device_init(mdev, type, &mgag200_g200_device_info); + if (ret) + return ERR_PTR(ret); + + vram_available = mgag200_device_probe_vram(mdev); + + ret = mgag200_modeset_init(mdev, vram_available); + if (ret) + return ERR_PTR(ret); + + return mdev; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_g200eh.c b/drivers/gpu/drm/mgag200/mgag200_g200eh.c new file mode 100644 index 000000000000..1b9a22728744 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_g200eh.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "mgag200_drv.h" + +/* + * DRM device + */ + +static const struct mgag200_device_info mgag200_g200eh_device_info = + MGAG200_DEVICE_INFO_INIT(2048, 2048, 37500, false, 1, 0, false); + +struct mga_device *mgag200_g200eh_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type) +{ + struct mga_device *mdev; + struct drm_device *dev; + resource_size_t vram_available; + int ret; + + mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base); + if (IS_ERR(mdev)) + return mdev; + dev = &mdev->base; + + pci_set_drvdata(pdev, dev); + + ret = mgag200_init_pci_options(pdev, 0x00000120, 0x0000b000); + if (ret) + return ERR_PTR(ret); + + ret = mgag200_device_preinit(mdev); + if (ret) + return ERR_PTR(ret); + + ret = mgag200_device_init(mdev, type, &mgag200_g200eh_device_info); + if (ret) + return ERR_PTR(ret); + + vram_available = mgag200_device_probe_vram(mdev); + + ret = mgag200_modeset_init(mdev, vram_available); + if (ret) + return ERR_PTR(ret); + + return mdev; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_g200eh3.c b/drivers/gpu/drm/mgag200/mgag200_g200eh3.c new file mode 100644 index 000000000000..438cda1b14c9 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_g200eh3.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "mgag200_drv.h" + +/* + * DRM device + */ + +static const struct mgag200_device_info mgag200_g200eh3_device_info = + MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, false, 1, 0, false); + +struct mga_device *mgag200_g200eh3_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum mga_type type) +{ + struct mga_device *mdev; + struct drm_device *dev; + resource_size_t vram_available; + int ret; + + mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base); + if (IS_ERR(mdev)) + return mdev; + dev = &mdev->base; + + pci_set_drvdata(pdev, dev); + + ret = mgag200_init_pci_options(pdev, 0x00000120, 0x0000b000); + if (ret) + return ERR_PTR(ret); + + ret = mgag200_device_preinit(mdev); + if (ret) + return ERR_PTR(ret); + + ret = mgag200_device_init(mdev, type, &mgag200_g200eh3_device_info); + if (ret) + return ERR_PTR(ret); + + vram_available = mgag200_device_probe_vram(mdev); + + ret = mgag200_modeset_init(mdev, vram_available); + if (ret) + return ERR_PTR(ret); + + return mdev; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_g200er.c b/drivers/gpu/drm/mgag200/mgag200_g200er.c new file mode 100644 index 000000000000..0790d4e6463d --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_g200er.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "mgag200_drv.h" + +/* + * DRM device + */ + +static const struct mgag200_device_info mgag200_g200er_device_info = + MGAG200_DEVICE_INFO_INIT(2048, 2048, 55000, false, 1, 0, false); + +struct mga_device *mgag200_g200er_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type) +{ + struct mga_device *mdev; + struct drm_device *dev; + resource_size_t vram_available; + int ret; + + mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base); + if (IS_ERR(mdev)) + return mdev; + dev = &mdev->base; + + pci_set_drvdata(pdev, dev); + + ret = mgag200_device_preinit(mdev); + if (ret) + return ERR_PTR(ret); + + ret = mgag200_device_init(mdev, type, &mgag200_g200er_device_info); + if (ret) + return ERR_PTR(ret); + + vram_available = mgag200_device_probe_vram(mdev); + + ret = mgag200_modeset_init(mdev, vram_available); + if (ret) + return ERR_PTR(ret); + + return mdev; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_g200ev.c b/drivers/gpu/drm/mgag200/mgag200_g200ev.c new file mode 100644 index 000000000000..5353422d0eef --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_g200ev.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "mgag200_drv.h" + +/* + * DRM device + */ + +static const struct mgag200_device_info mgag200_g200ev_device_info = + MGAG200_DEVICE_INFO_INIT(2048, 2048, 32700, false, 0, 1, false); + +struct mga_device *mgag200_g200ev_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type) +{ + struct mga_device *mdev; + struct drm_device *dev; + resource_size_t vram_available; + int ret; + + mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base); + if (IS_ERR(mdev)) + return mdev; + dev = &mdev->base; + + pci_set_drvdata(pdev, dev); + + ret = mgag200_init_pci_options(pdev, 0x00000120, 0x0000b000); + if (ret) + return ERR_PTR(ret); + + ret = mgag200_device_preinit(mdev); + if (ret) + return ERR_PTR(ret); + + ret = mgag200_device_init(mdev, type, &mgag200_g200ev_device_info); + if (ret) + return ERR_PTR(ret); + + vram_available = mgag200_device_probe_vram(mdev); + + ret = mgag200_modeset_init(mdev, vram_available); + if (ret) + return ERR_PTR(ret); + + return mdev; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_g200ew3.c b/drivers/gpu/drm/mgag200/mgag200_g200ew3.c new file mode 100644 index 000000000000..3bfc1324cf78 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_g200ew3.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "mgag200_drv.h" + +/* + * DRM device + */ + +static const struct mgag200_device_info mgag200_g200ew3_device_info = + MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, true, 0, 1, false); + +static resource_size_t mgag200_g200ew3_device_probe_vram(struct mga_device *mdev) +{ + resource_size_t vram_size = resource_size(mdev->vram_res); + + if (vram_size >= 0x1000000) + vram_size = vram_size - 0x400000; + return mgag200_probe_vram(mdev->vram, vram_size); +} + +struct mga_device *mgag200_g200ew3_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum mga_type type) +{ + struct mga_device *mdev; + struct drm_device *dev; + resource_size_t vram_available; + int ret; + + mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base); + if (IS_ERR(mdev)) + return mdev; + dev = &mdev->base; + + pci_set_drvdata(pdev, dev); + + ret = mgag200_init_pci_options(pdev, 0x41049120, 0x0000b000); + if (ret) + return ERR_PTR(ret); + + ret = mgag200_device_preinit(mdev); + if (ret) + return ERR_PTR(ret); + + ret = mgag200_device_init(mdev, type, &mgag200_g200ew3_device_info); + if (ret) + return ERR_PTR(ret); + + vram_available = mgag200_g200ew3_device_probe_vram(mdev); + + ret = mgag200_modeset_init(mdev, vram_available); + if (ret) + return ERR_PTR(ret); + + return mdev; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_g200se.c b/drivers/gpu/drm/mgag200/mgag200_g200se.c new file mode 100644 index 000000000000..0a3e66695e22 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_g200se.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "mgag200_drv.h" + +static int mgag200_g200se_init_pci_options(struct pci_dev *pdev) +{ + struct device *dev = &pdev->dev; + bool has_sgram; + u32 option; + int err; + + err = pci_read_config_dword(pdev, PCI_MGA_OPTION, &option); + if (err != PCIBIOS_SUCCESSFUL) { + dev_err(dev, "pci_read_config_dword(PCI_MGA_OPTION) failed: %d\n", err); + return pcibios_err_to_errno(err); + } + + has_sgram = !!(option & PCI_MGA_OPTION_HARDPWMSK); + + option = 0x40049120; + if (has_sgram) + option |= PCI_MGA_OPTION_HARDPWMSK; + + return mgag200_init_pci_options(pdev, option, 0x00008000); +} + +/* + * DRM device + */ + +static const struct mgag200_device_info mgag200_g200se_a_01_device_info = + MGAG200_DEVICE_INFO_INIT(1600, 1200, 24400, false, 0, 1, true); + +static const struct mgag200_device_info mgag200_g200se_a_02_device_info = + MGAG200_DEVICE_INFO_INIT(1920, 1200, 30100, false, 0, 1, true); + +static const struct mgag200_device_info mgag200_g200se_a_03_device_info = + MGAG200_DEVICE_INFO_INIT(2048, 2048, 55000, false, 0, 1, false); + +static const struct mgag200_device_info mgag200_g200se_b_01_device_info = + MGAG200_DEVICE_INFO_INIT(1600, 1200, 24400, false, 0, 1, false); + +static const struct mgag200_device_info mgag200_g200se_b_02_device_info = + MGAG200_DEVICE_INFO_INIT(1920, 1200, 30100, false, 0, 1, false); + +static const struct mgag200_device_info mgag200_g200se_b_03_device_info = + MGAG200_DEVICE_INFO_INIT(2048, 2048, 55000, false, 0, 1, false); + +static int mgag200_g200se_init_unique_rev_id(struct mgag200_g200se_device *g200se) +{ + struct mga_device *mdev = &g200se->base; + struct drm_device *dev = &mdev->base; + + /* stash G200 SE model number for later use */ + g200se->unique_rev_id = RREG32(0x1e24); + if (!g200se->unique_rev_id) + return -ENODEV; + + drm_dbg(dev, "G200 SE unique revision id is 0x%x\n", g200se->unique_rev_id); + + return 0; +} + +struct mga_device *mgag200_g200se_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type) +{ + struct mgag200_g200se_device *g200se; + const struct mgag200_device_info *info; + struct mga_device *mdev; + struct drm_device *dev; + resource_size_t vram_available; + int ret; + + g200se = devm_drm_dev_alloc(&pdev->dev, drv, struct mgag200_g200se_device, base.base); + if (IS_ERR(g200se)) + return ERR_CAST(g200se); + mdev = &g200se->base; + dev = &mdev->base; + + pci_set_drvdata(pdev, dev); + + ret = mgag200_g200se_init_pci_options(pdev); + if (ret) + return ERR_PTR(ret); + + ret = mgag200_device_preinit(mdev); + if (ret) + return ERR_PTR(ret); + + ret = mgag200_g200se_init_unique_rev_id(g200se); + if (ret) + return ERR_PTR(ret); + + switch (type) { + case G200_SE_A: + if (g200se->unique_rev_id >= 0x03) + info = &mgag200_g200se_a_03_device_info; + else if (g200se->unique_rev_id >= 0x02) + info = &mgag200_g200se_a_02_device_info; + else + info = &mgag200_g200se_a_01_device_info; + break; + case G200_SE_B: + if (g200se->unique_rev_id >= 0x03) + info = &mgag200_g200se_b_03_device_info; + else if (g200se->unique_rev_id >= 0x02) + info = &mgag200_g200se_b_02_device_info; + else + info = &mgag200_g200se_b_01_device_info; + break; + default: + return ERR_PTR(-EINVAL); + } + + ret = mgag200_device_init(mdev, type, info); + if (ret) + return ERR_PTR(ret); + + vram_available = mgag200_device_probe_vram(mdev); + + ret = mgag200_modeset_init(mdev, vram_available); + if (ret) + return ERR_PTR(ret); + + return mdev; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_g200wb.c b/drivers/gpu/drm/mgag200/mgag200_g200wb.c new file mode 100644 index 000000000000..c8450ac8eaec --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_g200wb.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "mgag200_drv.h" + +/* + * DRM device + */ + +static const struct mgag200_device_info mgag200_g200wb_device_info = + MGAG200_DEVICE_INFO_INIT(1280, 1024, 31877, true, 0, 1, false); + +struct mga_device *mgag200_g200wb_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type) +{ + struct mga_device *mdev; + struct drm_device *dev; + resource_size_t vram_available; + int ret; + + mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base); + if (IS_ERR(mdev)) + return mdev; + dev = &mdev->base; + + pci_set_drvdata(pdev, dev); + + ret = mgag200_init_pci_options(pdev, 0x41049120, 0x0000b000); + if (ret) + return ERR_PTR(ret); + + ret = mgag200_device_preinit(mdev); + if (ret) + return ERR_PTR(ret); + + ret = mgag200_device_init(mdev, type, &mgag200_g200wb_device_info); + if (ret) + return ERR_PTR(ret); + + vram_available = mgag200_device_probe_vram(mdev); + + ret = mgag200_modeset_init(mdev, vram_available); + if (ret) + return ERR_PTR(ret); + + return mdev; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_i2c.c b/drivers/gpu/drm/mgag200/mgag200_i2c.c index ac8e34eef513..0c48bdf3e7f8 100644 --- a/drivers/gpu/drm/mgag200/mgag200_i2c.c +++ b/drivers/gpu/drm/mgag200/mgag200_i2c.c @@ -86,44 +86,25 @@ static int mga_gpio_getscl(void *data) return (mga_i2c_read_gpio(mdev) & i2c->clock) ? 1 : 0; } -struct mga_i2c_chan *mgag200_i2c_create(struct drm_device *dev) +static void mgag200_i2c_release(void *res) { - struct mga_device *mdev = to_mga_device(dev); - struct mga_i2c_chan *i2c; + struct mga_i2c_chan *i2c = res; + + i2c_del_adapter(&i2c->adapter); +} + +int mgag200_i2c_init(struct mga_device *mdev, struct mga_i2c_chan *i2c) +{ + struct drm_device *dev = &mdev->base; + const struct mgag200_device_info *info = mdev->info; int ret; - int data, clock; WREG_DAC(MGA1064_GEN_IO_CTL2, 1); WREG_DAC(MGA1064_GEN_IO_DATA, 0xff); WREG_DAC(MGA1064_GEN_IO_CTL, 0); - switch (mdev->type) { - case G200_SE_A: - case G200_SE_B: - case G200_EV: - case G200_WB: - case G200_EW3: - data = 1; - clock = 2; - break; - case G200_EH: - case G200_EH3: - case G200_ER: - data = 2; - clock = 1; - break; - default: - data = 2; - clock = 8; - break; - } - - i2c = kzalloc(sizeof(struct mga_i2c_chan), GFP_KERNEL); - if (!i2c) - return NULL; - - i2c->data = data; - i2c->clock = clock; + i2c->data = BIT(info->i2c.data_bit); + i2c->clock = BIT(info->i2c.clock_bit); i2c->adapter.owner = THIS_MODULE; i2c->adapter.class = I2C_CLASS_DDC; i2c->adapter.dev.parent = dev->dev; @@ -142,18 +123,8 @@ struct mga_i2c_chan *mgag200_i2c_create(struct drm_device *dev) i2c->bit.getscl = mga_gpio_getscl; ret = i2c_bit_add_bus(&i2c->adapter); - if (ret) { - kfree(i2c); - i2c = NULL; - } - return i2c; -} + if (ret) + return ret; -void mgag200_i2c_destroy(struct mga_i2c_chan *i2c) -{ - if (!i2c) - return; - i2c_del_adapter(&i2c->adapter); - kfree(i2c); + return devm_add_action_or_reset(dev->dev, mgag200_i2c_release, i2c); } - diff --git a/drivers/gpu/drm/mgag200/mgag200_mm.c b/drivers/gpu/drm/mgag200/mgag200_mm.c deleted file mode 100644 index fa996d46feed..000000000000 --- a/drivers/gpu/drm/mgag200/mgag200_mm.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2012 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - */ -/* - * Authors: Dave Airlie <airlied@redhat.com> - */ - -#include <linux/pci.h> - -#include <drm/drm_managed.h> - -#include "mgag200_drv.h" - -static size_t mgag200_probe_vram(struct mga_device *mdev, void __iomem *mem, - size_t size) -{ - int offset; - int orig; - int test1, test2; - int orig1, orig2; - size_t vram_size; - - /* Probe */ - orig = ioread16(mem); - iowrite16(0, mem); - - vram_size = size; - - if ((mdev->type == G200_EW3) && (vram_size >= 0x1000000)) - vram_size = vram_size - 0x400000; - - for (offset = 0x100000; offset < vram_size; offset += 0x4000) { - orig1 = ioread8(mem + offset); - orig2 = ioread8(mem + offset + 0x100); - - iowrite16(0xaa55, mem + offset); - iowrite16(0xaa55, mem + offset + 0x100); - - test1 = ioread16(mem + offset); - test2 = ioread16(mem); - - iowrite16(orig1, mem + offset); - iowrite16(orig2, mem + offset + 0x100); - - if (test1 != 0xaa55) - break; - - if (test2) - break; - } - - iowrite16(orig, mem); - - return offset - 65536; -} - -int mgag200_mm_init(struct mga_device *mdev) -{ - struct drm_device *dev = &mdev->base; - struct pci_dev *pdev = to_pci_dev(dev->dev); - u8 misc; - resource_size_t start, len; - - WREG_ECRT(0x04, 0x00); - - misc = RREG8(MGA_MISC_IN); - misc |= MGAREG_MISC_RAMMAPEN | - MGAREG_MISC_HIGH_PG_SEL; - WREG8(MGA_MISC_OUT, misc); - - /* BAR 0 is VRAM */ - start = pci_resource_start(pdev, 0); - len = pci_resource_len(pdev, 0); - - if (!devm_request_mem_region(dev->dev, start, len, "mgadrmfb_vram")) { - drm_err(dev, "can't reserve VRAM\n"); - return -ENXIO; - } - - /* Don't fail on errors, but performance might be reduced. */ - devm_arch_io_reserve_memtype_wc(dev->dev, start, len); - devm_arch_phys_wc_add(dev->dev, start, len); - - mdev->vram = devm_ioremap(dev->dev, start, len); - if (!mdev->vram) - return -ENOMEM; - - mdev->mc.vram_size = mgag200_probe_vram(mdev, mdev->vram, len); - mdev->mc.vram_base = start; - mdev->mc.vram_window = len; - - mdev->vram_fb_available = mdev->mc.vram_size; - - return 0; -} diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index abde7655477d..e339f50d6bc8 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -32,57 +32,78 @@ * This file contains setup code for the CRTC. */ -static void mga_crtc_load_lut(struct drm_crtc *crtc) +static void mgag200_crtc_set_gamma_linear(struct mga_device *mdev, + const struct drm_format_info *format) { - struct drm_device *dev = crtc->dev; - struct mga_device *mdev = to_mga_device(dev); - struct drm_framebuffer *fb; - u16 *r_ptr, *g_ptr, *b_ptr; int i; - if (!crtc->enabled) - return; - - if (!mdev->display_pipe.plane.state) - return; + WREG8(DAC_INDEX + MGA1064_INDEX, 0); - fb = mdev->display_pipe.plane.state->fb; + switch (format->format) { + case DRM_FORMAT_RGB565: + /* Use better interpolation, to take 32 values from 0 to 255 */ + for (i = 0; i < MGAG200_LUT_SIZE / 8; i++) { + WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 8 + i / 4); + WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 4 + i / 16); + WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 8 + i / 4); + } + /* Green has one more bit, so add padding with 0 for red and blue. */ + for (i = MGAG200_LUT_SIZE / 8; i < MGAG200_LUT_SIZE / 4; i++) { + WREG8(DAC_INDEX + MGA1064_COL_PAL, 0); + WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 4 + i / 16); + WREG8(DAC_INDEX + MGA1064_COL_PAL, 0); + } + break; + case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: + for (i = 0; i < MGAG200_LUT_SIZE; i++) { + WREG8(DAC_INDEX + MGA1064_COL_PAL, i); + WREG8(DAC_INDEX + MGA1064_COL_PAL, i); + WREG8(DAC_INDEX + MGA1064_COL_PAL, i); + } + break; + default: + drm_warn_once(&mdev->base, "Unsupported format %p4cc for gamma correction\n", + &format->format); + break; + } +} - r_ptr = crtc->gamma_store; - g_ptr = r_ptr + crtc->gamma_size; - b_ptr = g_ptr + crtc->gamma_size; +static void mgag200_crtc_set_gamma(struct mga_device *mdev, + const struct drm_format_info *format, + struct drm_color_lut *lut) +{ + int i; WREG8(DAC_INDEX + MGA1064_INDEX, 0); - if (fb && fb->format->cpp[0] * 8 == 16) { - int inc = (fb->format->depth == 15) ? 8 : 4; - u8 r, b; - for (i = 0; i < MGAG200_LUT_SIZE; i += inc) { - if (fb->format->depth == 16) { - if (i > (MGAG200_LUT_SIZE >> 1)) { - r = b = 0; - } else { - r = *r_ptr++ >> 8; - b = *b_ptr++ >> 8; - r_ptr++; - b_ptr++; - } - } else { - r = *r_ptr++ >> 8; - b = *b_ptr++ >> 8; - } - /* VGA registers */ - WREG8(DAC_INDEX + MGA1064_COL_PAL, r); - WREG8(DAC_INDEX + MGA1064_COL_PAL, *g_ptr++ >> 8); - WREG8(DAC_INDEX + MGA1064_COL_PAL, b); + switch (format->format) { + case DRM_FORMAT_RGB565: + /* Use better interpolation, to take 32 values from lut[0] to lut[255] */ + for (i = 0; i < MGAG200_LUT_SIZE / 8; i++) { + WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 8 + i / 4].red >> 8); + WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 4 + i / 16].green >> 8); + WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 8 + i / 4].blue >> 8); } - return; - } - for (i = 0; i < MGAG200_LUT_SIZE; i++) { - /* VGA registers */ - WREG8(DAC_INDEX + MGA1064_COL_PAL, *r_ptr++ >> 8); - WREG8(DAC_INDEX + MGA1064_COL_PAL, *g_ptr++ >> 8); - WREG8(DAC_INDEX + MGA1064_COL_PAL, *b_ptr++ >> 8); + /* Green has one more bit, so add padding with 0 for red and blue. */ + for (i = MGAG200_LUT_SIZE / 8; i < MGAG200_LUT_SIZE / 4; i++) { + WREG8(DAC_INDEX + MGA1064_COL_PAL, 0); + WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 4 + i / 16].green >> 8); + WREG8(DAC_INDEX + MGA1064_COL_PAL, 0); + } + break; + case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: + for (i = 0; i < MGAG200_LUT_SIZE; i++) { + WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].red >> 8); + WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].green >> 8); + WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].blue >> 8); + } + break; + default: + drm_warn_once(&mdev->base, "Unsupported format %p4cc for gamma correction\n", + &format->format); + break; } } @@ -223,6 +244,9 @@ static void mgag200_set_startadd(struct mga_device *mdev, startadd = offset / 8; + if (startadd > 0) + drm_WARN_ON_ONCE(dev, mdev->info->bug_no_startadd); + /* * Can't store addresses any higher than that, but we also * don't have more than 16 MiB of memory, so it should be fine. @@ -353,6 +377,7 @@ static void mgag200_init_regs(struct mga_device *mdev) static void mgag200_set_mode_regs(struct mga_device *mdev, const struct drm_display_mode *mode) { + const struct mgag200_device_info *info = mdev->info; unsigned int hdisplay, hsyncstart, hsyncend, htotal; unsigned int vdisplay, vsyncstart, vsyncend, vtotal; u8 misc, crtcext1, crtcext2, crtcext5; @@ -387,9 +412,9 @@ static void mgag200_set_mode_regs(struct mga_device *mdev, ((hdisplay & 0x100) >> 7) | ((hsyncstart & 0x100) >> 6) | (htotal & 0x40); - if (mdev->type == G200_WB || mdev->type == G200_EW3) - crtcext1 |= BIT(7) | /* vrsten */ - BIT(3); /* hrsten */ + if (info->has_vidrst) + crtcext1 |= MGAREG_CRTCEXT1_VRSTEN | + MGAREG_CRTCEXT1_HRSTEN; crtcext2 = ((vtotal & 0xc00) >> 10) | ((vdisplay & 0x400) >> 8) | @@ -559,13 +584,13 @@ static void mgag200_g200se_set_hiprilvl(struct mga_device *mdev, const struct drm_display_mode *mode, const struct drm_framebuffer *fb) { - u32 unique_rev_id = mdev->model.g200se.unique_rev_id; + struct mgag200_g200se_device *g200se = to_mgag200_g200se_device(&mdev->base); unsigned int hiprilvl; u8 crtcext6; - if (unique_rev_id >= 0x04) { + if (g200se->unique_rev_id >= 0x04) { hiprilvl = 0; - } else if (unique_rev_id >= 0x02) { + } else if (g200se->unique_rev_id >= 0x02) { unsigned int bpp; unsigned long mb; @@ -590,7 +615,7 @@ static void mgag200_g200se_set_hiprilvl(struct mga_device *mdev, else hiprilvl = 5; - } else if (unique_rev_id >= 0x01) { + } else if (g200se->unique_rev_id >= 0x01) { hiprilvl = 3; } else { hiprilvl = 4; @@ -665,176 +690,34 @@ static void mgag200_disable_display(struct mga_device *mdev) * Connector */ -static int mga_vga_get_modes(struct drm_connector *connector) -{ - struct mga_connector *mga_connector = to_mga_connector(connector); - struct edid *edid; - int ret = 0; - - edid = drm_get_edid(connector, &mga_connector->i2c->adapter); - if (edid) { - drm_connector_update_edid_property(connector, edid); - ret = drm_add_edid_modes(connector, edid); - kfree(edid); - } - return ret; -} - -static uint32_t mga_vga_calculate_mode_bandwidth(struct drm_display_mode *mode, - int bits_per_pixel) -{ - uint32_t total_area, divisor; - uint64_t active_area, pixels_per_second, bandwidth; - uint64_t bytes_per_pixel = (bits_per_pixel + 7) / 8; - - divisor = 1024; - - if (!mode->htotal || !mode->vtotal || !mode->clock) - return 0; - - active_area = mode->hdisplay * mode->vdisplay; - total_area = mode->htotal * mode->vtotal; - - pixels_per_second = active_area * mode->clock * 1000; - do_div(pixels_per_second, total_area); - - bandwidth = pixels_per_second * bytes_per_pixel * 100; - do_div(bandwidth, divisor); - - return (uint32_t)(bandwidth); -} - -#define MODE_BANDWIDTH MODE_BAD - -static enum drm_mode_status mga_vga_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) +static int mgag200_vga_connector_helper_get_modes(struct drm_connector *connector) { - struct drm_device *dev = connector->dev; - struct mga_device *mdev = to_mga_device(dev); - int bpp = 32; - - if (IS_G200_SE(mdev)) { - u32 unique_rev_id = mdev->model.g200se.unique_rev_id; - - if (unique_rev_id == 0x01) { - if (mode->hdisplay > 1600) - return MODE_VIRTUAL_X; - if (mode->vdisplay > 1200) - return MODE_VIRTUAL_Y; - if (mga_vga_calculate_mode_bandwidth(mode, bpp) - > (24400 * 1024)) - return MODE_BANDWIDTH; - } else if (unique_rev_id == 0x02) { - if (mode->hdisplay > 1920) - return MODE_VIRTUAL_X; - if (mode->vdisplay > 1200) - return MODE_VIRTUAL_Y; - if (mga_vga_calculate_mode_bandwidth(mode, bpp) - > (30100 * 1024)) - return MODE_BANDWIDTH; - } else { - if (mga_vga_calculate_mode_bandwidth(mode, bpp) - > (55000 * 1024)) - return MODE_BANDWIDTH; - } - } else if (mdev->type == G200_WB) { - if (mode->hdisplay > 1280) - return MODE_VIRTUAL_X; - if (mode->vdisplay > 1024) - return MODE_VIRTUAL_Y; - if (mga_vga_calculate_mode_bandwidth(mode, bpp) > - (31877 * 1024)) - return MODE_BANDWIDTH; - } else if (mdev->type == G200_EV && - (mga_vga_calculate_mode_bandwidth(mode, bpp) - > (32700 * 1024))) { - return MODE_BANDWIDTH; - } else if (mdev->type == G200_EH && - (mga_vga_calculate_mode_bandwidth(mode, bpp) - > (37500 * 1024))) { - return MODE_BANDWIDTH; - } else if (mdev->type == G200_ER && - (mga_vga_calculate_mode_bandwidth(mode, - bpp) > (55000 * 1024))) { - return MODE_BANDWIDTH; - } - - if ((mode->hdisplay % 8) != 0 || (mode->hsync_start % 8) != 0 || - (mode->hsync_end % 8) != 0 || (mode->htotal % 8) != 0) { - return MODE_H_ILLEGAL; - } - - if (mode->crtc_hdisplay > 2048 || mode->crtc_hsync_start > 4096 || - mode->crtc_hsync_end > 4096 || mode->crtc_htotal > 4096 || - mode->crtc_vdisplay > 2048 || mode->crtc_vsync_start > 4096 || - mode->crtc_vsync_end > 4096 || mode->crtc_vtotal > 4096) { - return MODE_BAD; - } - - /* Validate the mode input by the user */ - if (connector->cmdline_mode.specified) { - if (connector->cmdline_mode.bpp_specified) - bpp = connector->cmdline_mode.bpp; - } - - if ((mode->hdisplay * mode->vdisplay * (bpp/8)) > mdev->vram_fb_available) { - if (connector->cmdline_mode.specified) - connector->cmdline_mode.specified = false; - return MODE_BAD; - } + struct mga_device *mdev = to_mga_device(connector->dev); + int ret; - return MODE_OK; -} + /* + * Protect access to I/O registers from concurrent modesetting + * by acquiring the I/O-register lock. + */ + mutex_lock(&mdev->rmmio_lock); + ret = drm_connector_helper_get_modes_from_ddc(connector); + mutex_unlock(&mdev->rmmio_lock); -static void mga_connector_destroy(struct drm_connector *connector) -{ - struct mga_connector *mga_connector = to_mga_connector(connector); - mgag200_i2c_destroy(mga_connector->i2c); - drm_connector_cleanup(connector); + return ret; } static const struct drm_connector_helper_funcs mga_vga_connector_helper_funcs = { - .get_modes = mga_vga_get_modes, - .mode_valid = mga_vga_mode_valid, + .get_modes = mgag200_vga_connector_helper_get_modes, }; static const struct drm_connector_funcs mga_vga_connector_funcs = { .reset = drm_atomic_helper_connector_reset, .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = mga_connector_destroy, + .destroy = drm_connector_cleanup, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int mgag200_vga_connector_init(struct mga_device *mdev) -{ - struct drm_device *dev = &mdev->base; - struct mga_connector *mconnector = &mdev->connector; - struct drm_connector *connector = &mconnector->base; - struct mga_i2c_chan *i2c; - int ret; - - i2c = mgag200_i2c_create(dev); - if (!i2c) - drm_warn(dev, "failed to add DDC bus\n"); - - ret = drm_connector_init_with_ddc(dev, connector, - &mga_vga_connector_funcs, - DRM_MODE_CONNECTOR_VGA, - &i2c->adapter); - if (ret) - goto err_mgag200_i2c_destroy; - drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs); - - mconnector->i2c = i2c; - - return 0; - -err_mgag200_i2c_destroy: - mgag200_i2c_destroy(i2c); - return ret; -} - /* * Simple Display Pipe */ @@ -843,6 +726,30 @@ static enum drm_mode_status mgag200_simple_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe, const struct drm_display_mode *mode) { + struct mga_device *mdev = to_mga_device(pipe->crtc.dev); + const struct mgag200_device_info *info = mdev->info; + + /* + * Some devices have additional limits on the size of the + * display mode. + */ + if (mode->hdisplay > info->max_hdisplay) + return MODE_VIRTUAL_X; + if (mode->vdisplay > info->max_vdisplay) + return MODE_VIRTUAL_Y; + + if ((mode->hdisplay % 8) != 0 || (mode->hsync_start % 8) != 0 || + (mode->hsync_end % 8) != 0 || (mode->htotal % 8) != 0) { + return MODE_H_ILLEGAL; + } + + if (mode->crtc_hdisplay > 2048 || mode->crtc_hsync_start > 4096 || + mode->crtc_hsync_end > 4096 || mode->crtc_htotal > 4096 || + mode->crtc_vdisplay > 2048 || mode->crtc_vsync_start > 4096 || + mode->crtc_vsync_end > 4096 || mode->crtc_vtotal > 4096) { + return MODE_BAD; + } + return MODE_OK; } @@ -855,10 +762,6 @@ mgag200_handle_damage(struct mga_device *mdev, struct drm_framebuffer *fb, dst += drm_fb_clip_offset(fb->pitches[0], fb->format, clip); drm_fb_memcpy_toio(dst, fb->pitches[0], vmap, fb, clip); - - /* Always scanout image at VRAM offset 0 */ - mgag200_set_startadd(mdev, (u32)0); - mgag200_set_offset(mdev, fb); } static void @@ -908,11 +811,19 @@ mgag200_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe, if (mdev->type == G200_WB || mdev->type == G200_EW3) mgag200_g200wb_release_bmc(mdev); - mga_crtc_load_lut(crtc); + if (crtc_state->gamma_lut) + mgag200_crtc_set_gamma(mdev, fb->format, crtc_state->gamma_lut->data); + else + mgag200_crtc_set_gamma_linear(mdev, fb->format); + mgag200_enable_display(mdev); mgag200_handle_damage(mdev, fb, &fullscreen, &shadow_plane_state->data[0]); + /* Always scanout image at VRAM offset 0 */ + mgag200_set_startadd(mdev, (u32)0); + mgag200_set_offset(mdev, fb); + mutex_unlock(&mdev->rmmio_lock); } @@ -955,6 +866,14 @@ mgag200_simple_display_pipe_check(struct drm_simple_display_pipe *pipe, return ret; } + if (crtc_state->color_mgmt_changed && crtc_state->gamma_lut) { + if (crtc_state->gamma_lut->length != + MGAG200_LUT_SIZE * sizeof(struct drm_color_lut)) { + drm_err(dev, "Wrong size for gamma_lut %zu\n", + crtc_state->gamma_lut->length); + return -EINVAL; + } + } return 0; } @@ -963,20 +882,30 @@ mgag200_simple_display_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_state) { struct drm_plane *plane = &pipe->plane; + struct drm_crtc *crtc = &pipe->crtc; struct drm_device *dev = plane->dev; struct mga_device *mdev = to_mga_device(dev); struct drm_plane_state *state = plane->state; struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); struct drm_framebuffer *fb = state->fb; struct drm_rect damage; + struct drm_atomic_helper_damage_iter iter; if (!fb) return; mutex_lock(&mdev->rmmio_lock); - if (drm_atomic_helper_damage_merged(old_state, state, &damage)) + if (crtc->state->color_mgmt_changed && crtc->state->gamma_lut) + mgag200_crtc_set_gamma(mdev, fb->format, crtc->state->gamma_lut->data); + + drm_atomic_helper_damage_iter_init(&iter, old_state, state); + drm_atomic_for_each_plane_damage(&iter, &damage) { mgag200_handle_damage(mdev, fb, &damage, &shadow_plane_state->data[0]); + } + /* Always scanout image at VRAM offset 0 */ + mgag200_set_startadd(mdev, (u32)0); + mgag200_set_offset(mdev, fb); mutex_unlock(&mdev->rmmio_lock); } @@ -1056,30 +985,81 @@ static const uint64_t mgag200_simple_display_pipe_fmtmods[] = { * Mode config */ -static const struct drm_mode_config_funcs mgag200_mode_config_funcs = { - .fb_create = drm_gem_fb_create_with_dirty, - .atomic_check = drm_atomic_helper_check, - .atomic_commit = drm_atomic_helper_commit, -}; +/* Calculates a mode's required memory bandwidth (in KiB/sec). */ +static uint32_t mgag200_calculate_mode_bandwidth(const struct drm_display_mode *mode, + unsigned int bits_per_pixel) +{ + uint32_t total_area, divisor; + uint64_t active_area, pixels_per_second, bandwidth; + uint64_t bytes_per_pixel = (bits_per_pixel + 7) / 8; + + divisor = 1024; -static unsigned int mgag200_preferred_depth(struct mga_device *mdev) + if (!mode->htotal || !mode->vtotal || !mode->clock) + return 0; + + active_area = mode->hdisplay * mode->vdisplay; + total_area = mode->htotal * mode->vtotal; + + pixels_per_second = active_area * mode->clock * 1000; + do_div(pixels_per_second, total_area); + + bandwidth = pixels_per_second * bytes_per_pixel * 100; + do_div(bandwidth, divisor); + + return (uint32_t)bandwidth; +} + +static enum drm_mode_status mgag200_mode_config_mode_valid(struct drm_device *dev, + const struct drm_display_mode *mode) { - if (IS_G200_SE(mdev) && mdev->vram_fb_available < (2048*1024)) - return 16; - else - return 32; + static const unsigned int max_bpp = 4; // DRM_FORMAT_XRGB8888 + struct mga_device *mdev = to_mga_device(dev); + unsigned long fbsize, fbpages, max_fbpages; + const struct mgag200_device_info *info = mdev->info; + + max_fbpages = mdev->vram_available >> PAGE_SHIFT; + + fbsize = mode->hdisplay * mode->vdisplay * max_bpp; + fbpages = DIV_ROUND_UP(fbsize, PAGE_SIZE); + + if (fbpages > max_fbpages) + return MODE_MEM; + + /* + * Test the mode's required memory bandwidth if the device + * specifies a maximum. Not all devices do though. + */ + if (info->max_mem_bandwidth) { + uint32_t mode_bandwidth = mgag200_calculate_mode_bandwidth(mode, max_bpp * 8); + + if (mode_bandwidth > (info->max_mem_bandwidth * 1024)) + return MODE_BAD; + } + + return MODE_OK; } -int mgag200_modeset_init(struct mga_device *mdev) +static const struct drm_mode_config_funcs mgag200_mode_config_funcs = { + .fb_create = drm_gem_fb_create_with_dirty, + .mode_valid = mgag200_mode_config_mode_valid, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +int mgag200_modeset_init(struct mga_device *mdev, resource_size_t vram_available) { struct drm_device *dev = &mdev->base; - struct drm_connector *connector = &mdev->connector.base; + struct mga_i2c_chan *i2c = &mdev->i2c; + struct drm_connector *connector = &mdev->connector; struct drm_simple_display_pipe *pipe = &mdev->display_pipe; size_t format_count = ARRAY_SIZE(mgag200_simple_display_pipe_formats); int ret; mgag200_init_regs(mdev); + mdev->vram_available = vram_available; + ret = drmm_mode_config_init(dev); if (ret) { drm_err(dev, "drmm_mode_config_init() failed, error %d\n", @@ -1089,21 +1069,26 @@ int mgag200_modeset_init(struct mga_device *mdev) dev->mode_config.max_width = MGAG200_MAX_FB_WIDTH; dev->mode_config.max_height = MGAG200_MAX_FB_HEIGHT; - - dev->mode_config.preferred_depth = mgag200_preferred_depth(mdev); - - dev->mode_config.fb_base = mdev->mc.vram_base; - + dev->mode_config.preferred_depth = 24; + dev->mode_config.fb_base = mdev->vram_res->start; dev->mode_config.funcs = &mgag200_mode_config_funcs; - ret = mgag200_vga_connector_init(mdev); + ret = mgag200_i2c_init(mdev, i2c); if (ret) { - drm_err(dev, - "mgag200_vga_connector_init() failed, error %d\n", - ret); + drm_err(dev, "failed to add DDC bus: %d\n", ret); return ret; } + ret = drm_connector_init_with_ddc(dev, connector, + &mga_vga_connector_funcs, + DRM_MODE_CONNECTOR_VGA, + &i2c->adapter); + if (ret) { + drm_err(dev, "drm_connector_init_with_ddc() failed: %d\n", ret); + return ret; + } + drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs); + ret = mgag200_pixpll_init(&mdev->pixpll, mdev); if (ret) return ret; @@ -1121,9 +1106,13 @@ int mgag200_modeset_init(struct mga_device *mdev) return ret; } - /* FIXME: legacy gamma tables; convert to CRTC state */ + drm_plane_enable_fb_damage_clips(&pipe->plane); + + /* FIXME: legacy gamma tables, but atomic gamma doesn't work without */ drm_mode_crtc_set_gamma_size(&pipe->crtc, MGAG200_LUT_SIZE); + drm_crtc_enable_color_mgmt(&pipe->crtc, 0, false, MGAG200_LUT_SIZE); + drm_mode_config_reset(dev); return 0; diff --git a/drivers/gpu/drm/mgag200/mgag200_pll.c b/drivers/gpu/drm/mgag200/mgag200_pll.c index 52be08b744ad..8065ca5d8de9 100644 --- a/drivers/gpu/drm/mgag200/mgag200_pll.c +++ b/drivers/gpu/drm/mgag200/mgag200_pll.c @@ -13,6 +13,7 @@ static int mgag200_pixpll_compute_g200(struct mgag200_pll *pixpll, long clock, { struct mga_device *mdev = pixpll->mdev; struct drm_device *dev = &mdev->base; + struct mgag200_g200_device *g200 = to_mgag200_g200_device(dev); const int post_div_max = 7; const int in_div_min = 1; const int in_div_max = 6; @@ -23,9 +24,9 @@ static int mgag200_pixpll_compute_g200(struct mgag200_pll *pixpll, long clock, long f_vco; long computed; long delta, tmp_delta; - long ref_clk = mdev->model.g200.ref_clk; - long p_clk_min = mdev->model.g200.pclk_min; - long p_clk_max = mdev->model.g200.pclk_max; + long ref_clk = g200->ref_clk; + long p_clk_min = g200->pclk_min; + long p_clk_max = g200->pclk_max; if (clock > p_clk_max) { drm_err(dev, "Pixel Clock %ld too high\n", clock); @@ -951,6 +952,7 @@ static const struct mgag200_pll_funcs mgag200_pixpll_funcs_g200ew3 = { int mgag200_pixpll_init(struct mgag200_pll *pixpll, struct mga_device *mdev) { struct drm_device *dev = &mdev->base; + struct mgag200_g200se_device *g200se; pixpll->mdev = mdev; @@ -961,7 +963,9 @@ int mgag200_pixpll_init(struct mgag200_pll *pixpll, struct mga_device *mdev) break; case G200_SE_A: case G200_SE_B: - if (mdev->model.g200se.unique_rev_id >= 0x04) + g200se = to_mgag200_g200se_device(dev); + + if (g200se->unique_rev_id >= 0x04) pixpll->funcs = &mgag200_pixpll_funcs_g200se_04; else pixpll->funcs = &mgag200_pixpll_funcs_g200se_00; diff --git a/drivers/gpu/drm/mgag200/mgag200_reg.h b/drivers/gpu/drm/mgag200/mgag200_reg.h index 60e705283fe8..99a9ab7d9119 100644 --- a/drivers/gpu/drm/mgag200/mgag200_reg.h +++ b/drivers/gpu/drm/mgag200/mgag200_reg.h @@ -252,8 +252,10 @@ #define MGAREG_CRTCEXT0_OFFSET_MASK GENMASK(5, 4) +#define MGAREG_CRTCEXT1_VRSTEN BIT(7) #define MGAREG_CRTCEXT1_VSYNCOFF BIT(5) #define MGAREG_CRTCEXT1_HSYNCOFF BIT(4) +#define MGAREG_CRTCEXT1_HRSTEN BIT(3) #define MGAREG_CRTCEXT3_MGAMODE BIT(7) diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 4f9b3aa5deda..5226323e55d3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -39,6 +39,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_probe_helper.h> #include <drm/drm_fb_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_atomic.h> @@ -605,6 +606,7 @@ nouveau_fbcon_fini(struct drm_device *dev) if (!drm->fbcon) return; + drm_kms_helper_poll_fini(dev); nouveau_fbcon_accel_fini(dev); nouveau_fbcon_destroy(dev, drm->fbcon); kfree(drm->fbcon); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c index 2ed528c065fa..ac9e122586bc 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c @@ -41,11 +41,9 @@ nvkm_device_tegra_power_up(struct nvkm_device_tegra *tdev) ret = clk_prepare_enable(tdev->clk); if (ret) goto err_clk; - if (tdev->clk_ref) { - ret = clk_prepare_enable(tdev->clk_ref); - if (ret) - goto err_clk_ref; - } + ret = clk_prepare_enable(tdev->clk_ref); + if (ret) + goto err_clk_ref; ret = clk_prepare_enable(tdev->clk_pwr); if (ret) goto err_clk_pwr; @@ -70,8 +68,7 @@ nvkm_device_tegra_power_up(struct nvkm_device_tegra *tdev) err_clamp: clk_disable_unprepare(tdev->clk_pwr); err_clk_pwr: - if (tdev->clk_ref) - clk_disable_unprepare(tdev->clk_ref); + clk_disable_unprepare(tdev->clk_ref); err_clk_ref: clk_disable_unprepare(tdev->clk); err_clk: @@ -87,8 +84,7 @@ nvkm_device_tegra_power_down(struct nvkm_device_tegra *tdev) int ret; clk_disable_unprepare(tdev->clk_pwr); - if (tdev->clk_ref) - clk_disable_unprepare(tdev->clk_ref); + clk_disable_unprepare(tdev->clk_ref); clk_disable_unprepare(tdev->clk); udelay(10); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gv100.c index 70e16a91ac12..faf0fe9f704c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gv100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gv100.c @@ -52,7 +52,7 @@ gv100_fifo_runlist_cgrp(struct nvkm_fifo_cgrp *cgrp, nvkm_wo32(memory, offset + 0xc, 0x00000000); } -const struct gk104_fifo_runlist_func +static const struct gk104_fifo_runlist_func gv100_fifo_runlist = { .size = 16, .cgrp = gv100_fifo_runlist_cgrp, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c index 397ff4fe9df8..f16eabf4f642 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c @@ -1922,8 +1922,8 @@ gf100_gr_oneinit_tiles(struct gf100_gr *gr) for (i = 0; i < gr->gpc_nr; i++) { init_frac[i] = gr->tpc_nr[gpc_map[i]] * gr->gpc_nr * mul_factor; - init_err[i] = i * gr->tpc_max * mul_factor - comm_denom/2; - run_err[i] = init_frac[i] + init_err[i]; + init_err[i] = i * gr->tpc_max * mul_factor - comm_denom/2; + run_err[i] = init_frac[i] + init_err[i]; } for (i = 0; i < gr->tpc_total;) { diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c index 64e423dddd9e..6c318e41bde0 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c @@ -33,7 +33,7 @@ nvbios_addr(struct nvkm_bios *bios, u32 *addr, u8 size) { u32 p = *addr; - if (*addr > bios->image0_size && bios->imaged_addr) { + if (*addr >= bios->image0_size && bios->imaged_addr) { *addr -= bios->image0_size; *addr += bios->imaged_addr; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c index 8bf00b396ec1..ca74775834dd 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c @@ -280,7 +280,7 @@ nvkm_vmm_unref_ptes(struct nvkm_vmm_iter *it, bool pfn, u32 ptei, u32 ptes) if (desc->type == SPT && (pgt->refs[0] || pgt->refs[1])) nvkm_vmm_unref_sptes(it, pgt, desc, ptei, ptes); - /* PT no longer neeed? Destroy it. */ + /* PT no longer needed? Destroy it. */ if (!pgt->refs[type]) { it->lvl++; TRA(it, "%s empty", nvkm_vmm_desc_type(desc)); diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 38799effd00a..4f1f004b3c54 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -438,6 +438,8 @@ config DRM_PANEL_SAMSUNG_ATNA33XC20 depends on OF depends on BACKLIGHT_CLASS_DEVICE depends on PM + select DRM_DISPLAY_DP_HELPER + select DRM_DISPLAY_HELPER select DRM_DP_AUX_BUS help DRM panel driver for the Samsung ATNA33XC20 panel. This panel can't diff --git a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c index a9cd7135cb51..ee61d60eceae 100644 --- a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c +++ b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c @@ -209,7 +209,7 @@ static int feiyang_dsi_probe(struct mipi_dsi_device *dsi) return dev_err_probe(&dsi->dev, PTR_ERR(ctx->avdd), "Couldn't get avdd regulator\n"); - ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW); + ctx->reset = devm_gpiod_get_optional(&dsi->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ctx->reset)) return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset), "Couldn't get our reset GPIO\n"); diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c index 7f51a4682ccb..ee612303f076 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.c +++ b/drivers/gpu/drm/panfrost/panfrost_device.c @@ -11,6 +11,7 @@ #include "panfrost_device.h" #include "panfrost_devfreq.h" #include "panfrost_features.h" +#include "panfrost_issues.h" #include "panfrost_gpu.h" #include "panfrost_job.h" #include "panfrost_mmu.h" @@ -380,9 +381,13 @@ const char *panfrost_exception_name(u32 exception_code) bool panfrost_exception_needs_reset(const struct panfrost_device *pfdev, u32 exception_code) { - /* Right now, none of the GPU we support need a reset, but this - * might change. + /* If an occlusion query write causes a bus fault on affected GPUs, + * future fragment jobs may hang. Reset to workaround. */ + if (exception_code == DRM_PANFROST_EXCEPTION_JOB_BUS_FAULT) + return panfrost_has_hw_issue(pfdev, HW_ISSUE_TTRX_3076); + + /* No other GPUs we support need a reset */ return false; } diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index 087e69b98d06..c58075bc096e 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -665,6 +665,7 @@ static const struct of_device_id dt_match[] = { { .compatible = "arm,mali-t860", .data = &default_data, }, { .compatible = "arm,mali-t880", .data = &default_data, }, { .compatible = "arm,mali-bifrost", .data = &default_data, }, + { .compatible = "arm,mali-valhall-jm", .data = &default_data, }, { .compatible = "mediatek,mt8183-mali", .data = &mediatek_mt8183_data }, {} }; diff --git a/drivers/gpu/drm/panfrost/panfrost_features.h b/drivers/gpu/drm/panfrost/panfrost_features.h index 36fadcf9634e..7ed0cd3ea2d4 100644 --- a/drivers/gpu/drm/panfrost/panfrost_features.h +++ b/drivers/gpu/drm/panfrost/panfrost_features.h @@ -21,6 +21,7 @@ enum panfrost_hw_feature { HW_FEATURE_TLS_HASHING, HW_FEATURE_THREAD_GROUP_SPLIT, HW_FEATURE_IDVS_GROUP_SIZE, + HW_FEATURE_CLEAN_ONLY_SAFE, HW_FEATURE_3BIT_EXT_RW_L2_MMU_CONFIG, }; @@ -105,6 +106,18 @@ enum panfrost_hw_feature { BIT_ULL(HW_FEATURE_TLS_HASHING) | \ BIT_ULL(HW_FEATURE_3BIT_EXT_RW_L2_MMU_CONFIG)) +#define hw_features_g57 (\ + BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \ + BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \ + BIT_ULL(HW_FEATURE_XAFFINITY) | \ + BIT_ULL(HW_FEATURE_FLUSH_REDUCTION) | \ + BIT_ULL(HW_FEATURE_PROTECTED_MODE) | \ + BIT_ULL(HW_FEATURE_PROTECTED_DEBUG_MODE) | \ + BIT_ULL(HW_FEATURE_COHERENCY_REG) | \ + BIT_ULL(HW_FEATURE_AARCH64_MMU) | \ + BIT_ULL(HW_FEATURE_IDVS_GROUP_SIZE) | \ + BIT_ULL(HW_FEATURE_CLEAN_ONLY_SAFE)) + static inline bool panfrost_has_hw_feature(struct panfrost_device *pfdev, enum panfrost_hw_feature feat) { diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c index aa89926742fd..6452e4e900dd 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gpu.c +++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c @@ -108,6 +108,9 @@ static void panfrost_gpu_init_quirks(struct panfrost_device *pfdev) quirks |= SC_LS_ALLOW_ATTR_TYPES; } + if (panfrost_has_hw_issue(pfdev, HW_ISSUE_TTRX_2968_TTRX_3162)) + quirks |= SC_VAR_ALGORITHM; + if (panfrost_has_hw_feature(pfdev, HW_FEATURE_TLS_HASHING)) quirks |= SC_TLS_HASH_ENABLE; @@ -124,18 +127,6 @@ static void panfrost_gpu_init_quirks(struct panfrost_device *pfdev) gpu_write(pfdev, GPU_TILER_CONFIG, quirks); - quirks = gpu_read(pfdev, GPU_L2_MMU_CONFIG); - - /* Limit read & write ID width for AXI */ - if (panfrost_has_hw_feature(pfdev, HW_FEATURE_3BIT_EXT_RW_L2_MMU_CONFIG)) - quirks &= ~(L2_MMU_CONFIG_3BIT_LIMIT_EXTERNAL_READS | - L2_MMU_CONFIG_3BIT_LIMIT_EXTERNAL_WRITES); - else - quirks &= ~(L2_MMU_CONFIG_LIMIT_EXTERNAL_READS | - L2_MMU_CONFIG_LIMIT_EXTERNAL_WRITES); - - gpu_write(pfdev, GPU_L2_MMU_CONFIG, quirks); - quirks = 0; if ((panfrost_model_eq(pfdev, 0x860) || panfrost_model_eq(pfdev, 0x880)) && pfdev->features.revision >= 0x2000) @@ -210,6 +201,9 @@ static const struct panfrost_model gpu_models[] = { GPU_MODEL(g52, 0x7002), GPU_MODEL(g31, 0x7003, GPU_REV(g31, 1, 0)), + + GPU_MODEL(g57, 0x9001, + GPU_REV(g57, 0, 0)), }; static void panfrost_gpu_init_features(struct panfrost_device *pfdev) diff --git a/drivers/gpu/drm/panfrost/panfrost_issues.h b/drivers/gpu/drm/panfrost/panfrost_issues.h index 501a76c5e95f..eb60cb83667a 100644 --- a/drivers/gpu/drm/panfrost/panfrost_issues.h +++ b/drivers/gpu/drm/panfrost/panfrost_issues.h @@ -125,6 +125,16 @@ enum panfrost_hw_issue { * kernel must fiddle with L2 caches to prevent data leakage */ HW_ISSUE_TGOX_R1_1234, + /* Must set SC_VAR_ALGORITHM */ + HW_ISSUE_TTRX_2968_TTRX_3162, + + /* Bus fault from occlusion query write may cause future fragment jobs + * to hang */ + HW_ISSUE_TTRX_3076, + + /* Must issue a dummy job before starting real work to prevent hangs */ + HW_ISSUE_TTRX_3485, + HW_ISSUE_END }; @@ -248,7 +258,14 @@ enum panfrost_hw_issue { #define hw_issues_g76 0 -static inline bool panfrost_has_hw_issue(struct panfrost_device *pfdev, +#define hw_issues_g57 (\ + BIT_ULL(HW_ISSUE_TTRX_2968_TTRX_3162) | \ + BIT_ULL(HW_ISSUE_TTRX_3076)) + +#define hw_issues_g57_r0p0 (\ + BIT_ULL(HW_ISSUE_TTRX_3485)) + +static inline bool panfrost_has_hw_issue(const struct panfrost_device *pfdev, enum panfrost_hw_issue issue) { return test_bit(issue, pfdev->features.hw_issues); diff --git a/drivers/gpu/drm/panfrost/panfrost_regs.h b/drivers/gpu/drm/panfrost/panfrost_regs.h index 0b6cd8fdcb47..accb4fa3adb8 100644 --- a/drivers/gpu/drm/panfrost/panfrost_regs.h +++ b/drivers/gpu/drm/panfrost/panfrost_regs.h @@ -195,6 +195,7 @@ #define SC_TLS_HASH_ENABLE BIT(17) #define SC_LS_ATTR_CHECK_DISABLE BIT(18) #define SC_ENABLE_TEXGRD_FLAGS BIT(25) +#define SC_VAR_ALGORITHM BIT(29) /* End SHADER_CONFIG register */ /* TILER_CONFIG register */ diff --git a/drivers/gpu/drm/r128/r128_drv.h b/drivers/gpu/drm/r128/r128_drv.h index 2e1bc01aa5c9..970e192b0d51 100644 --- a/drivers/gpu/drm/r128/r128_drv.h +++ b/drivers/gpu/drm/r128/r128_drv.h @@ -300,8 +300,8 @@ extern long r128_compat_ioctl(struct file *filp, unsigned int cmd, # define R128_PM4_64PIO_128INDBM (5 << 28) # define R128_PM4_64BM_128INDBM (6 << 28) # define R128_PM4_64PIO_64VCBM_64INDBM (7 << 28) -# define R128_PM4_64BM_64VCBM_64INDBM (8 << 28) -# define R128_PM4_64PIO_64VCPIO_64INDPIO (15 << 28) +# define R128_PM4_64BM_64VCBM_64INDBM (8U << 28) +# define R128_PM4_64PIO_64VCPIO_64INDPIO (15U << 28) # define R128_PM4_BUFFER_CNTL_NOUPDATE (1 << 27) #define R128_PM4_BUFFER_WM_CNTL 0x0708 diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig index 5afab49dc4f2..53c2d9980d48 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig @@ -2,7 +2,6 @@ config DRM_ROCKCHIP tristate "DRM Support for Rockchip" depends on DRM && ROCKCHIP_IOMMU - select DRM_DISPLAY_HELPER if ROCKCHIP_ANALOGIX_DP select DRM_GEM_CMA_HELPER select DRM_KMS_HELPER select DRM_PANEL @@ -38,6 +37,7 @@ config ROCKCHIP_VOP2 config ROCKCHIP_ANALOGIX_DP bool "Rockchip specific extensions for Analogix DP driver" depends on ROCKCHIP_VOP + select DRM_DISPLAY_HELPER select DRM_DISPLAY_DP_HELPER help This selects support for Rockchip SoC specific extensions @@ -47,6 +47,8 @@ config ROCKCHIP_ANALOGIX_DP config ROCKCHIP_CDN_DP bool "Rockchip cdn DP" depends on EXTCON=y || (EXTCON=m && DRM_ROCKCHIP=m) + select DRM_DISPLAY_HELPER + select DRM_DISPLAY_DP_HELPER help This selects support for Rockchip SoC specific extensions for the cdn DP driver. If you want to enable Dp on diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 26ac91db0f35..6b72894e02d4 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -1202,7 +1202,7 @@ static void vop2_plane_atomic_update(struct drm_plane *plane, */ stride = (fb->pitches[0] << 3) / bpp; if ((stride & 0x3f) && (xmirror || rotate_90 || rotate_270)) - drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligened\n", + drm_err(vop2->drm, "vp%d %s stride[%d] not 64 pixel aligned\n", vp->id, win->data->name, stride); rb_swap = vop2_afbc_rb_swap(fb->format->format); @@ -1473,7 +1473,7 @@ static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id, default: drm_err(vop2->drm, "Invalid interface id %d on vp%d\n", id, vp->id); return; - }; + } dip |= RK3568_DSP_IF_POL__CFG_DONE_IMD; @@ -1524,6 +1524,7 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, if (ret < 0) { drm_err(vop2->drm, "failed to enable dclk for video port%d - %d\n", vp->id, ret); + vop2_unlock(vop2); return; } diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c index 191c56064f19..6b25b2f4f5a3 100644 --- a/drivers/gpu/drm/scheduler/sched_entity.c +++ b/drivers/gpu/drm/scheduler/sched_entity.c @@ -190,7 +190,7 @@ long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout) } EXPORT_SYMBOL(drm_sched_entity_flush); -static void drm_sched_entity_kill_jobs_irq_work(struct irq_work *wrk) +static void drm_sched_entity_kill_jobs_work(struct work_struct *wrk) { struct drm_sched_job *job = container_of(wrk, typeof(*job), work); @@ -207,8 +207,8 @@ static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f, struct drm_sched_job *job = container_of(cb, struct drm_sched_job, finish_cb); - init_irq_work(&job->work, drm_sched_entity_kill_jobs_irq_work); - irq_work_queue(&job->work); + INIT_WORK(&job->work, drm_sched_entity_kill_jobs_work); + schedule_work(&job->work); } static struct dma_fence * diff --git a/drivers/gpu/drm/solomon/ssd130x-spi.c b/drivers/gpu/drm/solomon/ssd130x-spi.c index 43722adab1f8..07802907e39a 100644 --- a/drivers/gpu/drm/solomon/ssd130x-spi.c +++ b/drivers/gpu/drm/solomon/ssd130x-spi.c @@ -143,6 +143,7 @@ static const struct of_device_id ssd130x_of_match[] = { }; MODULE_DEVICE_TABLE(of, ssd130x_of_match); +#if IS_MODULE(CONFIG_DRM_SSD130X_SPI) /* * The SPI core always reports a MODALIAS uevent of the form "spi:<dev>", even * if the device was registered via OF. This means that the module will not be @@ -160,6 +161,7 @@ static const struct spi_device_id ssd130x_spi_table[] = { { /* sentinel */ } }; MODULE_DEVICE_TABLE(spi, ssd130x_spi_table); +#endif static struct spi_driver ssd130x_spi_driver = { .driver = { diff --git a/drivers/gpu/drm/tiny/st7735r.c b/drivers/gpu/drm/tiny/st7735r.c index 29d618093e94..e0f02d367d88 100644 --- a/drivers/gpu/drm/tiny/st7735r.c +++ b/drivers/gpu/drm/tiny/st7735r.c @@ -174,6 +174,7 @@ MODULE_DEVICE_TABLE(of, st7735r_of_match); static const struct spi_device_id st7735r_id[] = { { "jd-t18003-t01", (uintptr_t)&jd_t18003_t01_cfg }, + { "rh128128t", (uintptr_t)&rh128128t_cfg }, { }, }; MODULE_DEVICE_TABLE(spi, st7735r_id); diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 75d308ec173d..296af2b89951 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -44,12 +44,6 @@ #include "ttm_module.h" -/* default destructor */ -static void ttm_bo_default_destroy(struct ttm_buffer_object *bo) -{ - kfree(bo); -} - static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo, struct ttm_placement *placement) { @@ -936,8 +930,7 @@ int ttm_bo_init_reserved(struct ttm_device *bdev, bool locked; int ret; - bo->destroy = destroy ? destroy : ttm_bo_default_destroy; - + bo->destroy = destroy; kref_init(&bo->kref); INIT_LIST_HEAD(&bo->ddestroy); bo->bdev = bdev; diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index 930574ad2bca..318fdb38b47a 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -128,7 +128,7 @@ struct drm_connector *udl_connector_init(struct drm_device *dev) connector = &udl_connector->connector; drm_connector_init(dev, connector, &udl_connector_funcs, - DRM_MODE_CONNECTOR_DVII); + DRM_MODE_CONNECTOR_VGA); drm_connector_helper_add(connector, &udl_connector_helper_funcs); connector->polled = DRM_CONNECTOR_POLL_HPD | diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index c6a1036bf2ea..914c0ac7dc8b 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -213,7 +213,7 @@ static int compose_active_planes(void **vaddr_out, * * Work handler for composing and computing CRCs. work_struct scheduled in * an ordered workqueue that's periodically scheduled to run by - * _vblank_handle() and flushed at vkms_atomic_crtc_destroy_state(). + * vkms_vblank_simulate() and flushed at vkms_atomic_commit_tail(). */ void vkms_composer_worker(struct work_struct *work) { diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c index eb94433067ba..85a66014c2b6 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c @@ -393,6 +393,12 @@ void vmw_bo_bo_free(struct ttm_buffer_object *bo) kfree(vmw_bo); } +/* default destructor */ +static void vmw_bo_default_destroy(struct ttm_buffer_object *bo) +{ + kfree(bo); +} + /** * vmw_bo_create_kernel - Create a pinned BO for internal kernel use. * @@ -425,7 +431,7 @@ int vmw_bo_create_kernel(struct vmw_private *dev_priv, unsigned long size, ret = ttm_bo_init_reserved(&dev_priv->bdev, bo, size, ttm_bo_type_kernel, placement, 0, - &ctx, NULL, NULL, NULL); + &ctx, NULL, NULL, vmw_bo_default_destroy); if (unlikely(ret)) goto error_free; @@ -448,6 +454,8 @@ int vmw_bo_create(struct vmw_private *vmw, { int ret; + BUG_ON(!bo_free); + *p_bo = kmalloc(sizeof(**p_bo), GFP_KERNEL); if (unlikely(!*p_bo)) { DRM_ERROR("Failed to allocate a buffer.\n"); |