diff options
author | Dave Airlie <airlied@redhat.com> | 2023-01-25 05:14:07 +0300 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2023-01-25 05:14:08 +0300 |
commit | 68de345e101ce9a24e5c8849e69dd0dba2e8c9b2 (patch) | |
tree | 557d8e826470864cc78cd0de4a12e49c82a17997 /drivers/gpu | |
parent | 7dd1be30f02f7115002fe00f1f6802bbcf79f857 (diff) | |
parent | 51affef35bb39f186aef7eeeb4a7f9ceccd3e65e (diff) | |
download | linux-68de345e101ce9a24e5c8849e69dd0dba2e8c9b2.tar.xz |
Merge tag 'drm-misc-next-2023-01-24' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for v6.3:
UAPI Changes:
Cross-subsystem Changes:
Core Changes:
* EDID: Improved mode parsing and refactoring
* fbdev: Cleanups
* format-helper: Add conversion from XRGB8888 to XBGR8888 and ABGR8888
Driver Changes:
* accel/ivpu: Add driver for Intel VPU accelerator
* bridge: Support i.MX93 LDB plus DT bindings
* exynos: Fixes
* panel: vtdr6130: Fixes; Support AUO A030JTN01 plus DT bindings
* simpledrm: Support system-memory framebuffers plus DT bindings
* ssd130x: Fix sparse warning
Signed-off-by: Dave Airlie <airlied@redhat.com>
# -----BEGIN PGP SIGNATURE-----
#
# iQEzBAABCAAdFiEEchf7rIzpz2NEoWjlaA3BHVMLeiMFAmPQN9YACgkQaA3BHVML
# eiNmmQf/bTV3oaMo55i3tYxhMCWYDtPVk+GGglDAykW7Lid8pvy6mJqJoW6uvgQF
# c6CcoY+6yG2WvnVLhXyhPaACiG5weQSdu3S/DdZ2nuJCb50YCwWNNKcu3qYnLVlz
# 2NQ/s0HN+Xvvy76GJFNarKlxSNADPWCNJ8wExAdBkWr7q8NiDfsWuMGrQRQORrm3
# zEkSJPKtWNHa+vmsQOO9yebD0LFx97CoU40FrVXZTtF0FugGIXjiknQwekzuFxdY
# fGBiFKsI+Y3s51gAppbmRRJ0jGLj3KDF5S+5GM8FNbgJQF67Wxttl/YtY6lJGcsa
# l0vpRoCe1ilhNVvoikzAu7UewkPKKA==
# =GLLt
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 25 Jan 2023 05:56:06 AEST
# gpg: using RSA key 7217FBAC8CE9CF6344A168E5680DC11D530B7A23
# gpg: Can't check signature: No public key
From: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/Y9A5ceDknyQixM3R@linux-uq9g
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/bridge/fsl-ldb.c | 57 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_atomic_state_helper.c | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_connector.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_edid.c | 528 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_format_helper.c | 66 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_probe_helper.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_dsi.c | 41 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/Kconfig | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-auo-a030jtn01.c | 308 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-visionox-vtdr6130.c | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/solomon/ssd130x.c | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/tiny/simpledrm.c | 110 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_dsi.c | 2 |
17 files changed, 863 insertions, 298 deletions
diff --git a/drivers/gpu/drm/bridge/fsl-ldb.c b/drivers/gpu/drm/bridge/fsl-ldb.c index 9bcba8fc57e7..6bac160b395b 100644 --- a/drivers/gpu/drm/bridge/fsl-ldb.c +++ b/drivers/gpu/drm/bridge/fsl-ldb.c @@ -18,7 +18,6 @@ #include <drm/drm_of.h> #include <drm/drm_panel.h> -#define LDB_CTRL 0x5c #define LDB_CTRL_CH0_ENABLE BIT(0) #define LDB_CTRL_CH0_DI_SELECT BIT(1) #define LDB_CTRL_CH1_ENABLE BIT(2) @@ -35,9 +34,13 @@ #define LDB_CTRL_ASYNC_FIFO_ENABLE BIT(24) #define LDB_CTRL_ASYNC_FIFO_THRESHOLD_MASK GENMASK(27, 25) -#define LVDS_CTRL 0x128 #define LVDS_CTRL_CH0_EN BIT(0) #define LVDS_CTRL_CH1_EN BIT(1) +/* + * LVDS_CTRL_LVDS_EN bit is poorly named in i.MX93 reference manual. + * Clear it to enable LVDS and set it to disable LVDS. + */ +#define LVDS_CTRL_LVDS_EN BIT(1) #define LVDS_CTRL_VBG_EN BIT(2) #define LVDS_CTRL_HS_EN BIT(3) #define LVDS_CTRL_PRE_EMPH_EN BIT(4) @@ -52,6 +55,29 @@ #define LVDS_CTRL_VBG_ADJ(n) (((n) & 0x7) << 17) #define LVDS_CTRL_VBG_ADJ_MASK GENMASK(19, 17) +enum fsl_ldb_devtype { + IMX8MP_LDB, + IMX93_LDB, +}; + +struct fsl_ldb_devdata { + u32 ldb_ctrl; + u32 lvds_ctrl; + bool lvds_en_bit; +}; + +static const struct fsl_ldb_devdata fsl_ldb_devdata[] = { + [IMX8MP_LDB] = { + .ldb_ctrl = 0x5c, + .lvds_ctrl = 0x128, + }, + [IMX93_LDB] = { + .ldb_ctrl = 0x20, + .lvds_ctrl = 0x24, + .lvds_en_bit = true, + }, +}; + struct fsl_ldb { struct device *dev; struct drm_bridge bridge; @@ -59,6 +85,7 @@ struct fsl_ldb { struct clk *clk; struct regmap *regmap; bool lvds_dual_link; + const struct fsl_ldb_devdata *devdata; }; static inline struct fsl_ldb *to_fsl_ldb(struct drm_bridge *bridge) @@ -173,12 +200,12 @@ static void fsl_ldb_atomic_enable(struct drm_bridge *bridge, reg |= LDB_CTRL_DI1_VSYNC_POLARITY; } - regmap_write(fsl_ldb->regmap, LDB_CTRL, reg); + regmap_write(fsl_ldb->regmap, fsl_ldb->devdata->ldb_ctrl, reg); /* Program LVDS_CTRL */ reg = LVDS_CTRL_CC_ADJ(2) | LVDS_CTRL_PRE_EMPH_EN | LVDS_CTRL_PRE_EMPH_ADJ(3) | LVDS_CTRL_VBG_EN; - regmap_write(fsl_ldb->regmap, LVDS_CTRL, reg); + regmap_write(fsl_ldb->regmap, fsl_ldb->devdata->lvds_ctrl, reg); /* Wait for VBG to stabilize. */ usleep_range(15, 20); @@ -187,7 +214,7 @@ static void fsl_ldb_atomic_enable(struct drm_bridge *bridge, if (fsl_ldb->lvds_dual_link) reg |= LVDS_CTRL_CH1_EN; - regmap_write(fsl_ldb->regmap, LVDS_CTRL, reg); + regmap_write(fsl_ldb->regmap, fsl_ldb->devdata->lvds_ctrl, reg); } static void fsl_ldb_atomic_disable(struct drm_bridge *bridge, @@ -195,9 +222,14 @@ static void fsl_ldb_atomic_disable(struct drm_bridge *bridge, { struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge); - /* Stop both channels. */ - regmap_write(fsl_ldb->regmap, LVDS_CTRL, 0); - regmap_write(fsl_ldb->regmap, LDB_CTRL, 0); + /* Stop channel(s). */ + if (fsl_ldb->devdata->lvds_en_bit) + /* Set LVDS_CTRL_LVDS_EN bit to disable. */ + regmap_write(fsl_ldb->regmap, fsl_ldb->devdata->lvds_ctrl, + LVDS_CTRL_LVDS_EN); + else + regmap_write(fsl_ldb->regmap, fsl_ldb->devdata->lvds_ctrl, 0); + regmap_write(fsl_ldb->regmap, fsl_ldb->devdata->ldb_ctrl, 0); clk_disable_unprepare(fsl_ldb->clk); } @@ -263,6 +295,10 @@ static int fsl_ldb_probe(struct platform_device *pdev) if (!fsl_ldb) return -ENOMEM; + fsl_ldb->devdata = of_device_get_match_data(dev); + if (!fsl_ldb->devdata) + return -EINVAL; + fsl_ldb->dev = &pdev->dev; fsl_ldb->bridge.funcs = &funcs; fsl_ldb->bridge.of_node = dev->of_node; @@ -321,7 +357,10 @@ static int fsl_ldb_remove(struct platform_device *pdev) } static const struct of_device_id fsl_ldb_match[] = { - { .compatible = "fsl,imx8mp-ldb", }, + { .compatible = "fsl,imx8mp-ldb", + .data = &fsl_ldb_devdata[IMX8MP_LDB], }, + { .compatible = "fsl,imx93-ldb", + .data = &fsl_ldb_devdata[IMX93_LDB], }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, fsl_ldb_match); diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 22251c5f6a8a..784e63d70a42 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -557,15 +557,15 @@ void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector) EXPORT_SYMBOL(drm_atomic_helper_connector_tv_reset); /** - * @drm_atomic_helper_connector_tv_check: Validate an analog TV connector state + * drm_atomic_helper_connector_tv_check - Validate an analog TV connector state * @connector: DRM Connector * @state: the DRM State object * * Checks the state object to see if the requested state is valid for an * analog TV connector. * - * Returns: - * Zero for success, a negative error code on error. + * Return: + * %0 for success, a negative error code on error. */ int drm_atomic_helper_connector_tv_check(struct drm_connector *connector, struct drm_atomic_state *state) diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 8d92777e57dd..9d0250c28e9b 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -565,6 +565,7 @@ void drm_connector_cleanup(struct drm_connector *connector) ida_free(&dev->mode_config.connector_ida, connector->index); kfree(connector->display_info.bus_formats); + kfree(connector->display_info.vics); drm_mode_object_unregister(dev, &connector->base); kfree(connector->name); connector->name = NULL; @@ -1846,7 +1847,7 @@ EXPORT_SYMBOL(drm_mode_create_tv_properties_legacy); * drm_mode_create_tv_properties - create TV specific connector properties * @dev: DRM device * @supported_tv_modes: Bitmask of TV modes supported (See DRM_MODE_TV_MODE_*) - + * * Called by a driver's TV initialization routine, this function creates * the TV specific connector properties for a given device. * diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 3841aba17abd..3d0a4da661bc 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -96,7 +96,6 @@ struct detailed_mode_closure { struct drm_connector *connector; const struct drm_edid *drm_edid; bool preferred; - u32 quirks; int modes; }; @@ -2887,9 +2886,9 @@ static u32 edid_get_quirks(const struct drm_edid *drm_edid) * Walk the mode list for connector, clearing the preferred status on existing * modes and setting it anew for the right mode ala quirks. */ -static void edid_fixup_preferred(struct drm_connector *connector, - u32 quirks) +static void edid_fixup_preferred(struct drm_connector *connector) { + const struct drm_display_info *info = &connector->display_info; struct drm_display_mode *t, *cur_mode, *preferred_mode; int target_refresh = 0; int cur_vrefresh, preferred_vrefresh; @@ -2897,9 +2896,9 @@ static void edid_fixup_preferred(struct drm_connector *connector, if (list_empty(&connector->probed_modes)) return; - if (quirks & EDID_QUIRK_PREFER_LARGE_60) + if (info->quirks & EDID_QUIRK_PREFER_LARGE_60) target_refresh = 60; - if (quirks & EDID_QUIRK_PREFER_LARGE_75) + if (info->quirks & EDID_QUIRK_PREFER_LARGE_75) target_refresh = 75; preferred_mode = list_first_entry(&connector->probed_modes, @@ -3401,9 +3400,9 @@ drm_mode_do_interlace_quirk(struct drm_display_mode *mode, */ static struct drm_display_mode *drm_mode_detailed(struct drm_connector *connector, const struct drm_edid *drm_edid, - const struct detailed_timing *timing, - u32 quirks) + const struct detailed_timing *timing) { + const struct drm_display_info *info = &connector->display_info; struct drm_device *dev = connector->dev; struct drm_display_mode *mode; const struct detailed_pixel_timing *pt = &timing->data.pixel_data; @@ -3437,7 +3436,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_connector *connecto return NULL; } - if (quirks & EDID_QUIRK_FORCE_REDUCED_BLANKING) { + if (info->quirks & EDID_QUIRK_FORCE_REDUCED_BLANKING) { mode = drm_cvt_mode(dev, hactive, vactive, 60, true, false, false); if (!mode) return NULL; @@ -3449,7 +3448,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_connector *connecto if (!mode) return NULL; - if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH) + if (info->quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH) mode->clock = 1088 * 10; else mode->clock = le16_to_cpu(timing->pixel_clock) * 10; @@ -3472,7 +3471,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_connector *connecto drm_mode_do_interlace_quirk(mode, pt); - if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) { + if (info->quirks & EDID_QUIRK_DETAILED_SYNC_PP) { mode->flags |= DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC; } else { mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ? @@ -3485,12 +3484,12 @@ set_size: mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4; mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8; - if (quirks & EDID_QUIRK_DETAILED_IN_CM) { + if (info->quirks & EDID_QUIRK_DETAILED_IN_CM) { mode->width_mm *= 10; mode->height_mm *= 10; } - if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) { + if (info->quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) { mode->width_mm = drm_edid->edid->width_cm * 10; mode->height_mm = drm_edid->edid->height_cm * 10; } @@ -4003,8 +4002,7 @@ do_detailed_mode(const struct detailed_timing *timing, void *c) return; newmode = drm_mode_detailed(closure->connector, - closure->drm_edid, timing, - closure->quirks); + closure->drm_edid, timing); if (!newmode) return; @@ -4027,15 +4025,13 @@ do_detailed_mode(const struct detailed_timing *timing, void *c) * add_detailed_modes - Add modes from detailed timings * @connector: attached connector * @drm_edid: EDID block to scan - * @quirks: quirks to apply */ static int add_detailed_modes(struct drm_connector *connector, - const struct drm_edid *drm_edid, u32 quirks) + const struct drm_edid *drm_edid) { struct detailed_mode_closure closure = { .connector = connector, .drm_edid = drm_edid, - .quirks = quirks, }; if (drm_edid->edid->revision >= 4) @@ -4468,28 +4464,20 @@ static u8 svd_to_vic(u8 svd) return svd; } +/* + * Return a display mode for the 0-based vic_index'th VIC across all CTA VDBs in + * the EDID, or NULL on errors. + */ static struct drm_display_mode * -drm_display_mode_from_vic_index(struct drm_connector *connector, - const u8 *video_db, u8 video_len, - u8 video_index) +drm_display_mode_from_vic_index(struct drm_connector *connector, int vic_index) { + const struct drm_display_info *info = &connector->display_info; struct drm_device *dev = connector->dev; - struct drm_display_mode *newmode; - u8 vic; - - if (video_db == NULL || video_index >= video_len) - return NULL; - /* CEA modes are numbered 1..127 */ - vic = svd_to_vic(video_db[video_index]); - if (!drm_valid_cea_vic(vic)) - return NULL; - - newmode = drm_mode_duplicate(dev, cea_mode_for_vic(vic)); - if (!newmode) + if (!info->vics || vic_index >= info->vics_len || !info->vics[vic_index]) return NULL; - return newmode; + return drm_display_mode_from_cea_vic(dev, info->vics[vic_index]); } /* @@ -4505,10 +4493,8 @@ drm_display_mode_from_vic_index(struct drm_connector *connector, static int do_y420vdb_modes(struct drm_connector *connector, const u8 *svds, u8 svds_len) { - int modes = 0, i; struct drm_device *dev = connector->dev; - struct drm_display_info *info = &connector->display_info; - struct drm_hdmi_info *hdmi = &info->hdmi; + int modes = 0, i; for (i = 0; i < svds_len; i++) { u8 vic = svd_to_vic(svds[i]); @@ -4520,35 +4506,13 @@ static int do_y420vdb_modes(struct drm_connector *connector, newmode = drm_mode_duplicate(dev, cea_mode_for_vic(vic)); if (!newmode) break; - bitmap_set(hdmi->y420_vdb_modes, vic, 1); drm_mode_probed_add(connector, newmode); modes++; } - if (modes > 0) - info->color_formats |= DRM_COLOR_FORMAT_YCBCR420; return modes; } -/* - * drm_add_cmdb_modes - Add a YCBCR 420 mode into bitmap - * @connector: connector corresponding to the HDMI sink - * @vic: CEA vic for the video mode to be added in the map - * - * Makes an entry for a videomode in the YCBCR 420 bitmap - */ -static void -drm_add_cmdb_modes(struct drm_connector *connector, u8 svd) -{ - u8 vic = svd_to_vic(svd); - struct drm_hdmi_info *hdmi = &connector->display_info.hdmi; - - if (!drm_valid_cea_vic(vic)) - return; - - bitmap_set(hdmi->y420_cmdb_modes, vic, 1); -} - /** * drm_display_mode_from_cea_vic() - return a mode for CEA VIC * @dev: DRM device @@ -4577,29 +4541,20 @@ drm_display_mode_from_cea_vic(struct drm_device *dev, } EXPORT_SYMBOL(drm_display_mode_from_cea_vic); -static int -do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len) +/* Add modes based on VICs parsed in parse_cta_vdb() */ +static int add_cta_vdb_modes(struct drm_connector *connector) { + const struct drm_display_info *info = &connector->display_info; int i, modes = 0; - struct drm_hdmi_info *hdmi = &connector->display_info.hdmi; - for (i = 0; i < len; i++) { + if (!info->vics) + return 0; + + for (i = 0; i < info->vics_len; i++) { struct drm_display_mode *mode; - mode = drm_display_mode_from_vic_index(connector, db, len, i); + mode = drm_display_mode_from_vic_index(connector, i); if (mode) { - /* - * YCBCR420 capability block contains a bitmap which - * gives the index of CEA modes from CEA VDB, which - * can support YCBCR 420 sampling output also (apart - * from RGB/YCBCR444 etc). - * For example, if the bit 0 in bitmap is set, - * first mode in VDB can support YCBCR420 output too. - * Add YCBCR420 modes only if sink is HDMI 2.0 capable. - */ - if (i < 64 && hdmi->y420_cmdb_map & (1ULL << i)) - drm_add_cmdb_modes(connector, db[i]); - drm_mode_probed_add(connector, mode); modes++; } @@ -4693,15 +4648,13 @@ static int add_hdmi_mode(struct drm_connector *connector, u8 vic) } static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, - const u8 *video_db, u8 video_len, u8 video_index) + int vic_index) { struct drm_display_mode *newmode; int modes = 0; if (structure & (1 << 0)) { - newmode = drm_display_mode_from_vic_index(connector, video_db, - video_len, - video_index); + newmode = drm_display_mode_from_vic_index(connector, vic_index); if (newmode) { newmode->flags |= DRM_MODE_FLAG_3D_FRAME_PACKING; drm_mode_probed_add(connector, newmode); @@ -4709,9 +4662,7 @@ static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, } } if (structure & (1 << 6)) { - newmode = drm_display_mode_from_vic_index(connector, video_db, - video_len, - video_index); + newmode = drm_display_mode_from_vic_index(connector, vic_index); if (newmode) { newmode->flags |= DRM_MODE_FLAG_3D_TOP_AND_BOTTOM; drm_mode_probed_add(connector, newmode); @@ -4719,9 +4670,7 @@ static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, } } if (structure & (1 << 8)) { - newmode = drm_display_mode_from_vic_index(connector, video_db, - video_len, - video_index); + newmode = drm_display_mode_from_vic_index(connector, vic_index); if (newmode) { newmode->flags |= DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF; drm_mode_probed_add(connector, newmode); @@ -4732,6 +4681,26 @@ static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, return modes; } +static bool hdmi_vsdb_latency_present(const u8 *db) +{ + return db[8] & BIT(7); +} + +static bool hdmi_vsdb_i_latency_present(const u8 *db) +{ + return hdmi_vsdb_latency_present(db) && db[8] & BIT(6); +} + +static int hdmi_vsdb_latency_length(const u8 *db) +{ + if (hdmi_vsdb_i_latency_present(db)) + return 4; + else if (hdmi_vsdb_latency_present(db)) + return 2; + else + return 0; +} + /* * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block * @connector: connector corresponding to the HDMI sink @@ -4742,10 +4711,8 @@ static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, * also adds the stereo 3d modes when applicable. */ static int -do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, - const u8 *video_db, u8 video_len) +do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len) { - struct drm_display_info *info = &connector->display_info; int modes = 0, offset = 0, i, multi_present = 0, multi_len; u8 vic_len, hdmi_3d_len = 0; u16 mask; @@ -4758,13 +4725,7 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, if (!(db[8] & (1 << 5))) goto out; - /* Latency_Fields_Present */ - if (db[8] & (1 << 7)) - offset += 2; - - /* I_Latency_Fields_Present */ - if (db[8] & (1 << 6)) - offset += 2; + offset += hdmi_vsdb_latency_length(db); /* the declared length is not long enough for the 2 first bytes * of additional video format capabilities */ @@ -4818,9 +4779,7 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, for (i = 0; i < 16; i++) { if (mask & (1 << i)) modes += add_3d_struct_modes(connector, - structure_all, - video_db, - video_len, i); + structure_all, i); } } @@ -4857,8 +4816,6 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, if (newflag != 0) { newmode = drm_display_mode_from_vic_index(connector, - video_db, - video_len, vic_index); if (newmode) { @@ -4873,8 +4830,6 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, } out: - if (modes > 0) - info->has_hdmi_infoframe = true; return modes; } @@ -5204,20 +5159,26 @@ static int edid_hfeeodb_extension_block_count(const struct edid *edid) return cta[4 + 2]; } -static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector, - const u8 *db) +/* + * CTA-861 YCbCr 4:2:0 Capability Map Data Block (CTA Y420CMDB) + * + * Y420CMDB contains a bitmap which gives the index of CTA modes from CTA VDB, + * which can support YCBCR 420 sampling output also (apart from RGB/YCBCR444 + * etc). For example, if the bit 0 in bitmap is set, first mode in VDB can + * support YCBCR420 output too. + */ +static void parse_cta_y420cmdb(struct drm_connector *connector, + const struct cea_db *db, u64 *y420cmdb_map) { struct drm_display_info *info = &connector->display_info; - struct drm_hdmi_info *hdmi = &info->hdmi; - u8 map_len = cea_db_payload_len(db) - 1; - u8 count; + int i, map_len = cea_db_payload_len(db) - 1; + const u8 *data = cea_db_data(db) + 1; u64 map = 0; if (map_len == 0) { /* All CEA modes support ycbcr420 sampling also.*/ - hdmi->y420_cmdb_map = U64_MAX; - info->color_formats |= DRM_COLOR_FORMAT_YCBCR420; - return; + map = U64_MAX; + goto out; } /* @@ -5235,13 +5196,14 @@ static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector, if (WARN_ON_ONCE(map_len > 8)) map_len = 8; - for (count = 0; count < map_len; count++) - map |= (u64)db[2 + count] << (8 * count); + for (i = 0; i < map_len; i++) + map |= (u64)data[i] << (8 * i); +out: if (map) info->color_formats |= DRM_COLOR_FORMAT_YCBCR420; - hdmi->y420_cmdb_map = map; + *y420cmdb_map = map; } static int add_cea_modes(struct drm_connector *connector, @@ -5249,21 +5211,16 @@ static int add_cea_modes(struct drm_connector *connector, { const struct cea_db *db; struct cea_db_iter iter; - int modes = 0; + int modes; + + /* CTA VDB block VICs parsed earlier */ + modes = add_cta_vdb_modes(connector); 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); + if (cea_db_is_hdmi_vsdb(db)) { + modes += do_hdmi_vsdb_modes(connector, (const u8 *)db, + cea_db_payload_len(db)); } else if (cea_db_is_y420vdb(db)) { const u8 *vdb420 = cea_db_data(db) + 1; @@ -5271,15 +5228,6 @@ static int add_cea_modes(struct drm_connector *connector, 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); } cea_db_iter_end(&iter); @@ -5416,6 +5364,7 @@ drm_parse_hdr_metadata_block(struct drm_connector *connector, const u8 *db) } } +/* HDMI Vendor-Specific Data Block (HDMI VSDB, H14b-VSDB) */ static void drm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db) { @@ -5423,18 +5372,18 @@ drm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db) if (len >= 6 && (db[6] & (1 << 7))) connector->eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_SUPPORTS_AI; - if (len >= 8) { - connector->latency_present[0] = db[8] >> 7; - connector->latency_present[1] = (db[8] >> 6) & 1; - } - if (len >= 9) + + if (len >= 10 && hdmi_vsdb_latency_present(db)) { + connector->latency_present[0] = true; connector->video_latency[0] = db[9]; - if (len >= 10) connector->audio_latency[0] = db[10]; - if (len >= 11) + } + + if (len >= 12 && hdmi_vsdb_i_latency_present(db)) { + connector->latency_present[1] = true; connector->video_latency[1] = db[11]; - if (len >= 12) connector->audio_latency[1] = db[12]; + } drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI: latency present %d %d, video latency %d %d, audio latency %d %d\n", @@ -5533,8 +5482,6 @@ static void drm_edid_to_eld(struct drm_connector *connector, int total_sad_count = 0; int mnl; - clear_eld(connector); - if (!drm_edid) return; @@ -5864,6 +5811,92 @@ drm_default_rgb_quant_range(const struct drm_display_mode *mode) } EXPORT_SYMBOL(drm_default_rgb_quant_range); +/* CTA-861 Video Data Block (CTA VDB) */ +static void parse_cta_vdb(struct drm_connector *connector, const struct cea_db *db) +{ + struct drm_display_info *info = &connector->display_info; + int i, vic_index, len = cea_db_payload_len(db); + const u8 *svds = cea_db_data(db); + u8 *vics; + + if (!len) + return; + + /* Gracefully handle multiple VDBs, however unlikely that is */ + vics = krealloc(info->vics, info->vics_len + len, GFP_KERNEL); + if (!vics) + return; + + vic_index = info->vics_len; + info->vics_len += len; + info->vics = vics; + + for (i = 0; i < len; i++) { + u8 vic = svd_to_vic(svds[i]); + + if (!drm_valid_cea_vic(vic)) + vic = 0; + + info->vics[vic_index++] = vic; + } +} + +/* + * Update y420_cmdb_modes based on previously parsed CTA VDB and Y420CMDB. + * + * Translate the y420cmdb_map based on VIC indexes to y420_cmdb_modes indexed + * using the VICs themselves. + */ +static void update_cta_y420cmdb(struct drm_connector *connector, u64 y420cmdb_map) +{ + struct drm_display_info *info = &connector->display_info; + struct drm_hdmi_info *hdmi = &info->hdmi; + int i, len = min_t(int, info->vics_len, BITS_PER_TYPE(y420cmdb_map)); + + for (i = 0; i < len; i++) { + u8 vic = info->vics[i]; + + if (vic && y420cmdb_map & BIT_ULL(i)) + bitmap_set(hdmi->y420_cmdb_modes, vic, 1); + } +} + +static bool cta_vdb_has_vic(const struct drm_connector *connector, u8 vic) +{ + const struct drm_display_info *info = &connector->display_info; + int i; + + if (!vic || !info->vics) + return false; + + for (i = 0; i < info->vics_len; i++) { + if (info->vics[i] == vic) + return true; + } + + return false; +} + +/* CTA-861-H YCbCr 4:2:0 Video Data Block (CTA Y420VDB) */ +static void parse_cta_y420vdb(struct drm_connector *connector, + const struct cea_db *db) +{ + struct drm_display_info *info = &connector->display_info; + struct drm_hdmi_info *hdmi = &info->hdmi; + const u8 *svds = cea_db_data(db) + 1; + int i; + + for (i = 0; i < cea_db_payload_len(db) - 1; i++) { + u8 vic = svd_to_vic(svds[i]); + + if (!drm_valid_cea_vic(vic)) + continue; + + bitmap_set(hdmi->y420_vdb_modes, vic, 1); + info->color_formats |= DRM_COLOR_FORMAT_YCBCR420; + } +} + static void drm_parse_vcdb(struct drm_connector *connector, const u8 *db) { struct drm_display_info *info = &connector->display_info; @@ -5995,14 +6028,14 @@ static void drm_parse_dsc_info(struct drm_hdmi_dsc_cap *hdmi_dsc, 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; + struct drm_display_info *info = &connector->display_info; + struct drm_hdmi_info *hdmi = &info->hdmi; struct drm_hdmi_dsc_cap *hdmi_dsc = &hdmi->dsc_cap; int max_tmds_clock = 0; u8 max_frl_rate = 0; bool dsc_support = false; - display->has_hdmi_infoframe = true; + info->has_hdmi_infoframe = true; if (hf_scds[6] & 0x80) { hdmi->scdc.supported = true; @@ -6026,7 +6059,7 @@ static void drm_parse_hdmi_forum_scds(struct drm_connector *connector, max_tmds_clock = hf_scds[5] * 5000; if (max_tmds_clock > 340000) { - display->max_tmds_clock = max_tmds_clock; + info->max_tmds_clock = max_tmds_clock; } if (scdc->supported) { @@ -6117,6 +6150,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector, } } +/* HDMI Vendor-Specific Data Block (HDMI VSDB, H14b-VSDB) */ static void drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db) { @@ -6130,6 +6164,15 @@ drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db) if (len >= 7) info->max_tmds_clock = db[7] * 5000; + /* + * Try to infer whether the sink supports HDMI infoframes. + * + * HDMI infoframe support was first added in HDMI 1.4. Assume the sink + * supports infoframes if HDMI_Video_present is set. + */ + if (len >= 8 && db[8] & BIT(5)) + info->has_hdmi_infoframe = true; + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI: DVI dual %d, max TMDS clock %d kHz\n", connector->base.id, connector->name, info->dvi_dual, info->max_tmds_clock); @@ -6165,6 +6208,7 @@ static void drm_parse_cea_ext(struct drm_connector *connector, const struct cea_db *db; struct cea_db_iter iter; const u8 *edid_ext; + u64 y420cmdb_map = 0; drm_edid_iter_begin(drm_edid, &edid_iter); drm_edid_iter_for_each(edid_ext, &edid_iter) { @@ -6202,13 +6246,20 @@ static void drm_parse_cea_ext(struct drm_connector *connector, 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); + parse_cta_y420cmdb(connector, db, &y420cmdb_map); + else if (cea_db_is_y420vdb(db)) + parse_cta_y420vdb(connector, db); 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); + else if (cea_db_tag(db) == CTA_DB_VIDEO) + parse_cta_vdb(connector, db); } cea_db_iter_end(&iter); + + if (y420cmdb_map) + update_cta_y420cmdb(connector, y420cmdb_map); } static @@ -6374,17 +6425,29 @@ static void drm_reset_display_info(struct drm_connector *connector) info->mso_stream_count = 0; info->mso_pixel_overlap = 0; info->max_dsc_bpp = 0; + + kfree(info->vics); + info->vics = NULL; + info->vics_len = 0; + + info->quirks = 0; } -static u32 update_display_info(struct drm_connector *connector, - const struct drm_edid *drm_edid) +static void 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(drm_edid); + const struct edid *edid; drm_reset_display_info(connector); + clear_eld(connector); + + if (!drm_edid) + return; + + edid = drm_edid->edid; + + info->quirks = edid_get_quirks(drm_edid); info->width_mm = edid->width_cm * 10; info->height_mm = edid->height_cm * 10; @@ -6456,17 +6519,30 @@ static u32 update_display_info(struct drm_connector *connector, drm_update_mso(connector, drm_edid); out: - if (quirks & EDID_QUIRK_NON_DESKTOP) { + if (info->quirks & EDID_QUIRK_NON_DESKTOP) { drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Non-desktop display%s\n", connector->base.id, connector->name, info->non_desktop ? " (redundant quirk)" : ""); info->non_desktop = true; } - if (quirks & EDID_QUIRK_CAP_DSC_15BPP) + if (info->quirks & EDID_QUIRK_CAP_DSC_15BPP) info->max_dsc_bpp = 15; - return quirks; + if (info->quirks & EDID_QUIRK_FORCE_6BPC) + info->bpc = 6; + + if (info->quirks & EDID_QUIRK_FORCE_8BPC) + info->bpc = 8; + + if (info->quirks & EDID_QUIRK_FORCE_10BPC) + info->bpc = 10; + + if (info->quirks & EDID_QUIRK_FORCE_12BPC) + info->bpc = 12; + + /* Depends on info->cea_rev set by drm_parse_cea_ext() above */ + drm_edid_to_eld(connector, drm_edid); } static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev, @@ -6561,27 +6637,14 @@ static int add_displayid_detailed_modes(struct drm_connector *connector, return num_modes; } -static int _drm_edid_connector_update(struct drm_connector *connector, - const struct drm_edid *drm_edid) +static int _drm_edid_connector_add_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid) { + const struct drm_display_info *info = &connector->display_info; int num_modes = 0; - u32 quirks; - if (!drm_edid) { - drm_reset_display_info(connector); - clear_eld(connector); + if (!drm_edid) return 0; - } - - /* - * 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 = 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: @@ -6597,7 +6660,7 @@ 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, drm_edid, quirks); + num_modes += add_detailed_modes(connector, drm_edid); num_modes += add_cvt_modes(connector, drm_edid); num_modes += add_standard_modes(connector, drm_edid); num_modes += add_established_modes(connector, drm_edid); @@ -6607,20 +6670,8 @@ static int _drm_edid_connector_update(struct drm_connector *connector, if (drm_edid->edid->features & DRM_EDID_FEATURE_CONTINUOUS_FREQ) 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); - - if (quirks & EDID_QUIRK_FORCE_6BPC) - connector->display_info.bpc = 6; - - if (quirks & EDID_QUIRK_FORCE_8BPC) - connector->display_info.bpc = 8; - - if (quirks & EDID_QUIRK_FORCE_10BPC) - connector->display_info.bpc = 10; - - if (quirks & EDID_QUIRK_FORCE_12BPC) - connector->display_info.bpc = 12; + if (info->quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) + edid_fixup_preferred(connector); return num_modes; } @@ -6684,49 +6735,54 @@ out: * @connector: Connector * @drm_edid: EDID * - * Update the connector mode list, display info, ELD, HDR metadata, relevant - * properties, etc. from the passed in EDID. + * Update the connector display info, ELD, HDR metadata, relevant properties, + * etc. from the passed in EDID. * * If EDID is NULL, reset the information. * - * Return: The number of modes added or 0 if we couldn't find any. + * Must be called before calling drm_edid_connector_add_modes(). + * + * Return: 0 on success, negative error on errors. */ int drm_edid_connector_update(struct drm_connector *connector, const struct drm_edid *drm_edid) { - int count; - - count = _drm_edid_connector_update(connector, drm_edid); + update_display_info(connector, drm_edid); _drm_update_tile_info(connector, drm_edid); - /* Note: Ignore errors for now. */ - _drm_edid_connector_property_update(connector, drm_edid); - - return count; + return _drm_edid_connector_property_update(connector, drm_edid); } EXPORT_SYMBOL(drm_edid_connector_update); -static int _drm_connector_update_edid_property(struct drm_connector *connector, - const struct drm_edid *drm_edid) +/** + * drm_edid_connector_add_modes - Update probed modes from the EDID property + * @connector: Connector + * + * Add the modes from the previously updated EDID property to the connector + * probed modes list. + * + * drm_edid_connector_update() must have been called before this to update the + * EDID property. + * + * Return: The number of modes added, or 0 if we couldn't find any. + */ +int drm_edid_connector_add_modes(struct drm_connector *connector) { - /* - * Set the display info, using edid if available, otherwise resetting - * the values to defaults. This duplicates the work done in - * drm_add_edid_modes, but that function is not consistently called - * before this one in all drivers and the computation is cheap enough - * that it seems better to duplicate it rather than attempt to ensure - * some arbitrary ordering of calls. - */ - if (drm_edid) - update_display_info(connector, drm_edid); - else - drm_reset_display_info(connector); + const struct drm_edid *drm_edid = NULL; + int count; - _drm_update_tile_info(connector, drm_edid); + if (connector->edid_blob_ptr) + drm_edid = drm_edid_alloc(connector->edid_blob_ptr->data, + connector->edid_blob_ptr->length); - return _drm_edid_connector_property_update(connector, drm_edid); + count = _drm_edid_connector_add_modes(connector, drm_edid); + + drm_edid_free(drm_edid); + + return count; } +EXPORT_SYMBOL(drm_edid_connector_add_modes); /** * drm_connector_update_edid_property - update the edid property of a connector @@ -6749,8 +6805,7 @@ int drm_connector_update_edid_property(struct drm_connector *connector, { struct drm_edid drm_edid; - return _drm_connector_update_edid_property(connector, - drm_edid_legacy_init(&drm_edid, edid)); + return drm_edid_connector_update(connector, drm_edid_legacy_init(&drm_edid, edid)); } EXPORT_SYMBOL(drm_connector_update_edid_property); @@ -6763,13 +6818,14 @@ EXPORT_SYMBOL(drm_connector_update_edid_property); * &drm_display_info structure and ELD in @connector with any information which * can be derived from the edid. * - * This function is deprecated. Use drm_edid_connector_update() instead. + * This function is deprecated. Use drm_edid_connector_add_modes() instead. * * Return: The number of modes added or 0 if we couldn't find any. */ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) { - struct drm_edid drm_edid; + struct drm_edid _drm_edid; + const struct drm_edid *drm_edid; if (edid && !drm_edid_is_valid(edid)) { drm_warn(connector->dev, "[CONNECTOR:%d:%s] EDID invalid.\n", @@ -6777,8 +6833,11 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) edid = NULL; } - return _drm_edid_connector_update(connector, - drm_edid_legacy_init(&drm_edid, edid)); + drm_edid = drm_edid_legacy_init(&_drm_edid, edid); + + update_display_info(connector, drm_edid); + + return _drm_edid_connector_add_modes(connector, drm_edid); } EXPORT_SYMBOL(drm_add_edid_modes); @@ -6885,8 +6944,6 @@ static u8 drm_mode_hdmi_vic(const struct drm_connector *connector, static u8 drm_mode_cea_vic(const struct drm_connector *connector, const struct drm_display_mode *mode) { - u8 vic; - /* * HDMI spec says if a mode is found in HDMI 1.4b 4K modes * we should send its VIC in vendor infoframes, else send the @@ -6896,14 +6953,23 @@ static u8 drm_mode_cea_vic(const struct drm_connector *connector, if (drm_mode_hdmi_vic(connector, mode)) return 0; - vic = drm_match_cea_mode(mode); + return drm_match_cea_mode(mode); +} - /* - * HDMI 1.4 VIC range: 1 <= VIC <= 64 (CEA-861-D) but - * HDMI 2.0 VIC range: 1 <= VIC <= 107 (CEA-861-F). So we - * have to make sure we dont break HDMI 1.4 sinks. - */ - if (!is_hdmi2_sink(connector) && vic > 64) +/* + * Avoid sending VICs defined in HDMI 2.0 in AVI infoframes to sinks that + * conform to HDMI 1.4. + * + * HDMI 1.4 (CTA-861-D) VIC range: [1..64] + * HDMI 2.0 (CTA-861-F) VIC range: [1..107] + * + * If the sink lists the VIC in CTA VDB, assume it's fine, regardless of HDMI + * version. + */ +static u8 vic_for_avi_infoframe(const struct drm_connector *connector, u8 vic) +{ + if (!is_hdmi2_sink(connector) && vic > 64 && + !cta_vdb_has_vic(connector, vic)) return 0; return vic; @@ -6978,7 +7044,7 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, picture_aspect = HDMI_PICTURE_ASPECT_NONE; } - frame->video_code = vic; + frame->video_code = vic_for_avi_infoframe(connector, vic); frame->picture_aspect = picture_aspect; frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN; diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index 994f8fb71f45..f93a4efcee90 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -649,6 +649,66 @@ void drm_fb_xrgb8888_to_argb8888(struct iosys_map *dst, const unsigned int *dst_ } EXPORT_SYMBOL(drm_fb_xrgb8888_to_argb8888); +static void drm_fb_xrgb8888_to_abgr8888_line(void *dbuf, const void *sbuf, unsigned int pixels) +{ + __le32 *dbuf32 = dbuf; + const __le32 *sbuf32 = sbuf; + unsigned int x; + u32 pix; + + for (x = 0; x < pixels; x++) { + pix = le32_to_cpu(sbuf32[x]); + pix = ((pix & 0x00ff0000) >> 16) << 0 | + ((pix & 0x0000ff00) >> 8) << 8 | + ((pix & 0x000000ff) >> 0) << 16 | + GENMASK(31, 24); /* fill alpha bits */ + *dbuf32++ = cpu_to_le32(pix); + } +} + +static void drm_fb_xrgb8888_to_abgr8888(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, + const struct drm_framebuffer *fb, + const struct drm_rect *clip) +{ + static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { + 4, + }; + + drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, + drm_fb_xrgb8888_to_abgr8888_line); +} + +static void drm_fb_xrgb8888_to_xbgr8888_line(void *dbuf, const void *sbuf, unsigned int pixels) +{ + __le32 *dbuf32 = dbuf; + const __le32 *sbuf32 = sbuf; + unsigned int x; + u32 pix; + + for (x = 0; x < pixels; x++) { + pix = le32_to_cpu(sbuf32[x]); + pix = ((pix & 0x00ff0000) >> 16) << 0 | + ((pix & 0x0000ff00) >> 8) << 8 | + ((pix & 0x000000ff) >> 0) << 16 | + ((pix & 0xff000000) >> 24) << 24; + *dbuf32++ = cpu_to_le32(pix); + } +} + +static void drm_fb_xrgb8888_to_xbgr8888(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, + const struct drm_framebuffer *fb, + const struct drm_rect *clip) +{ + static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { + 4, + }; + + drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, + drm_fb_xrgb8888_to_xbgr8888_line); +} + static void drm_fb_xrgb8888_to_xrgb2101010_line(void *dbuf, const void *sbuf, unsigned int pixels) { __le32 *dbuf32 = dbuf; @@ -868,6 +928,12 @@ int drm_fb_blit(struct iosys_map *dst, const unsigned int *dst_pitch, uint32_t d } else if (dst_format == DRM_FORMAT_ARGB8888) { drm_fb_xrgb8888_to_argb8888(dst, dst_pitch, src, fb, clip); return 0; + } else if (dst_format == DRM_FORMAT_XBGR8888) { + drm_fb_xrgb8888_to_xbgr8888(dst, dst_pitch, src, fb, clip); + return 0; + } else if (dst_format == DRM_FORMAT_ABGR8888) { + drm_fb_xrgb8888_to_abgr8888(dst, dst_pitch, src, fb, clip); + return 0; } else if (dst_format == DRM_FORMAT_XRGB2101010) { drm_fb_xrgb8888_to_xrgb2101010(dst, dst_pitch, src, fb, clip); return 0; diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 7973f2589ced..95aeeed33cf5 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -1163,7 +1163,9 @@ int drm_connector_helper_get_modes(struct drm_connector *connector) * EDID. Otherwise, if the EDID is NULL, clear the connector * information. */ - count = drm_edid_connector_update(connector, drm_edid); + drm_edid_connector_update(connector, drm_edid); + + count = drm_edid_connector_add_modes(connector); drm_edid_free(drm_edid); diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index ec673223d6b7..320c370cfe24 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -75,10 +75,27 @@ #define DSIM_MAIN_PIX_FORMAT_RGB565 (0x4 << 12) #define DSIM_SUB_VC (((x) & 0x3) << 16) #define DSIM_MAIN_VC (((x) & 0x3) << 18) -#define DSIM_HSA_MODE (1 << 20) -#define DSIM_HBP_MODE (1 << 21) -#define DSIM_HFP_MODE (1 << 22) -#define DSIM_HSE_MODE (1 << 23) +#define DSIM_HSA_DISABLE_MODE (1 << 20) +#define DSIM_HBP_DISABLE_MODE (1 << 21) +#define DSIM_HFP_DISABLE_MODE (1 << 22) +/* + * The i.MX 8M Mini Applications Processor Reference Manual, + * Rev. 3, 11/2020 Page 4091 + * The i.MX 8M Nano Applications Processor Reference Manual, + * Rev. 2, 07/2022 Page 3058 + * The i.MX 8M Plus Applications Processor Reference Manual, + * Rev. 1, 06/2021 Page 5436 + * named this bit as 'HseDisableMode' but the bit definition + * is quite opposite like + * 0 = Disables transfer + * 1 = Enables transfer + * which clearly states that HSE is not a disable bit. + * + * This bit is named as per the manual even though it is not + * a disable bit however the driver logic for handling HSE + * is based on the MIPI_DSI_MODE_VIDEO_HSE flag itself. + */ +#define DSIM_HSE_DISABLE_MODE (1 << 23) #define DSIM_AUTO_MODE (1 << 24) #define DSIM_VIDEO_MODE (1 << 25) #define DSIM_BURST_MODE (1 << 26) @@ -804,16 +821,16 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi) if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT) reg |= DSIM_AUTO_MODE; if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSE) - reg |= DSIM_HSE_MODE; - if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HFP)) - reg |= DSIM_HFP_MODE; - if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HBP)) - reg |= DSIM_HBP_MODE; - if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HSA)) - reg |= DSIM_HSA_MODE; + reg |= DSIM_HSE_DISABLE_MODE; + if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HFP) + reg |= DSIM_HFP_DISABLE_MODE; + if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HBP) + reg |= DSIM_HBP_DISABLE_MODE; + if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HSA) + reg |= DSIM_HSA_DISABLE_MODE; } - if (!(dsi->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET)) + if (dsi->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET) reg |= DSIM_EOT_DISABLE; switch (dsi->format) { diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 8f2046caaa6b..8eeee71c0000 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -37,6 +37,14 @@ config DRM_PANEL_ASUS_Z00T_TM5P5_NT35596 NT35596 1080x1920 video mode panel as found in some Asus Zenfone 2 Laser Z00T devices. +config DRM_PANEL_AUO_A030JTN01 + tristate "AUO A030JTN01" + depends on SPI + select REGMAP_SPI + help + Say Y here to enable support for the AUO A030JTN01 320x480 3.0" panel + as found in the YLM RS-97 handheld gaming console. + config DRM_PANEL_BOE_BF060Y8M_AJ0 tristate "Boe BF060Y8M-AJ0 panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index d07e6fa28bdf..c05aa9e23907 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_DRM_PANEL_ABT_Y030XX067A) += panel-abt-y030xx067a.o obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o obj-$(CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596) += panel-asus-z00t-tm5p5-n35596.o +obj-$(CONFIG_DRM_PANEL_AUO_A030JTN01) += panel-auo-a030jtn01.o obj-$(CONFIG_DRM_PANEL_BOE_BF060Y8M_AJ0) += panel-boe-bf060y8m-aj0.o obj-$(CONFIG_DRM_PANEL_BOE_HIMAX8279D) += panel-boe-himax8279d.o obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o diff --git a/drivers/gpu/drm/panel/panel-auo-a030jtn01.c b/drivers/gpu/drm/panel/panel-auo-a030jtn01.c new file mode 100644 index 000000000000..3c976a98de6a --- /dev/null +++ b/drivers/gpu/drm/panel/panel-auo-a030jtn01.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AU Optronics A030JTN01.0 TFT LCD panel driver + * + * Copyright (C) 2023, Paul Cercueil <paul@crapouillou.net> + * Copyright (C) 2023, Christophe Branchereau <cbranchereau@gmail.com> + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/media-bus-format.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> + +#define REG05 0x05 +#define REG06 0x06 +#define REG07 0x07 + +#define REG05_STDBY BIT(0) +#define REG06_VBLK GENMASK(4, 0) +#define REG07_HBLK GENMASK(7, 0) + + +struct a030jtn01_info { + const struct drm_display_mode *display_modes; + unsigned int num_modes; + u16 width_mm, height_mm; + u32 bus_format, bus_flags; +}; + +struct a030jtn01 { + struct drm_panel panel; + struct spi_device *spi; + struct regmap *map; + + const struct a030jtn01_info *panel_info; + + struct regulator *supply; + struct gpio_desc *reset_gpio; +}; + +static inline struct a030jtn01 *to_a030jtn01(struct drm_panel *panel) +{ + return container_of(panel, struct a030jtn01, panel); +} + +static int a030jtn01_prepare(struct drm_panel *panel) +{ + struct a030jtn01 *priv = to_a030jtn01(panel); + struct device *dev = &priv->spi->dev; + unsigned int dummy; + int err; + + err = regulator_enable(priv->supply); + if (err) { + dev_err(dev, "Failed to enable power supply: %d\n", err); + return err; + } + + usleep_range(1000, 8000); + + /* Reset the chip */ + gpiod_set_value_cansleep(priv->reset_gpio, 1); + usleep_range(100, 8000); + gpiod_set_value_cansleep(priv->reset_gpio, 0); + usleep_range(2000, 8000); + + /* + * No idea why, but a register read (doesn't matter which) is needed to + * properly initialize the chip after a reset; otherwise, the colors + * will be wrong. It doesn't seem to be timing-related as a msleep(200) + * doesn't fix it. + */ + err = regmap_read(priv->map, REG05, &dummy); + if (err) + goto err_disable_regulator; + + /* Use (24 + 6) == 0x1e as the vertical back porch */ + err = regmap_write(priv->map, REG06, FIELD_PREP(REG06_VBLK, 0x1e)); + if (err) + goto err_disable_regulator; + + /* Use (42 + 30) * 3 == 0xd8 as the horizontal back porch */ + err = regmap_write(priv->map, REG07, FIELD_PREP(REG07_HBLK, 0xd8)); + if (err) + goto err_disable_regulator; + + return 0; + +err_disable_regulator: + gpiod_set_value_cansleep(priv->reset_gpio, 1); + regulator_disable(priv->supply); + return err; +} + +static int a030jtn01_unprepare(struct drm_panel *panel) +{ + struct a030jtn01 *priv = to_a030jtn01(panel); + + gpiod_set_value_cansleep(priv->reset_gpio, 1); + regulator_disable(priv->supply); + + return 0; +} + +static int a030jtn01_enable(struct drm_panel *panel) +{ + struct a030jtn01 *priv = to_a030jtn01(panel); + int ret; + + ret = regmap_set_bits(priv->map, REG05, REG05_STDBY); + if (ret) + return ret; + + /* Wait for the picture to be stable */ + if (panel->backlight) + msleep(100); + + return 0; +} + +static int a030jtn01_disable(struct drm_panel *panel) +{ + struct a030jtn01 *priv = to_a030jtn01(panel); + + return regmap_clear_bits(priv->map, REG05, REG05_STDBY); +} + +static int a030jtn01_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct a030jtn01 *priv = to_a030jtn01(panel); + const struct a030jtn01_info *panel_info = priv->panel_info; + struct drm_display_mode *mode; + unsigned int i; + + for (i = 0; i < panel_info->num_modes; i++) { + mode = drm_mode_duplicate(connector->dev, + &panel_info->display_modes[i]); + if (!mode) + return -ENOMEM; + + drm_mode_set_name(mode); + + mode->type = DRM_MODE_TYPE_DRIVER; + if (panel_info->num_modes == 1) + mode->type |= DRM_MODE_TYPE_PREFERRED; + + drm_mode_probed_add(connector, mode); + } + + connector->display_info.bpc = 8; + connector->display_info.width_mm = panel_info->width_mm; + connector->display_info.height_mm = panel_info->height_mm; + + drm_display_info_set_bus_formats(&connector->display_info, + &panel_info->bus_format, 1); + connector->display_info.bus_flags = panel_info->bus_flags; + + return panel_info->num_modes; +} + +static const struct drm_panel_funcs a030jtn01_funcs = { + .prepare = a030jtn01_prepare, + .unprepare = a030jtn01_unprepare, + .enable = a030jtn01_enable, + .disable = a030jtn01_disable, + .get_modes = a030jtn01_get_modes, +}; + +static bool a030jtn01_has_reg(struct device *dev, unsigned int reg) +{ + static const u32 a030jtn01_regs_mask = 0x001823f1fb; + + return a030jtn01_regs_mask & BIT(reg); +}; + +static const struct regmap_config a030jtn01_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = 0x40, + .max_register = 0x1c, + .readable_reg = a030jtn01_has_reg, + .writeable_reg = a030jtn01_has_reg, +}; + +static int a030jtn01_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct a030jtn01 *priv; + int err; + + spi->mode |= SPI_MODE_3 | SPI_3WIRE; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->spi = spi; + spi_set_drvdata(spi, priv); + + priv->map = devm_regmap_init_spi(spi, &a030jtn01_regmap_config); + if (IS_ERR(priv->map)) + return dev_err_probe(dev, PTR_ERR(priv->map), "Unable to init regmap"); + + priv->panel_info = spi_get_device_match_data(spi); + if (!priv->panel_info) + return -EINVAL; + + priv->supply = devm_regulator_get(dev, "power"); + if (IS_ERR(priv->supply)) + return dev_err_probe(dev, PTR_ERR(priv->supply), "Failed to get power supply"); + + priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(priv->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO"); + + drm_panel_init(&priv->panel, dev, &a030jtn01_funcs, + DRM_MODE_CONNECTOR_DPI); + + err = drm_panel_of_backlight(&priv->panel); + if (err) + return err; + + drm_panel_add(&priv->panel); + + return 0; +} + +static void a030jtn01_remove(struct spi_device *spi) +{ + struct a030jtn01 *priv = spi_get_drvdata(spi); + + drm_panel_remove(&priv->panel); + drm_panel_disable(&priv->panel); + drm_panel_unprepare(&priv->panel); +} + +static const struct drm_display_mode a030jtn01_modes[] = { + { /* 60 Hz */ + .clock = 14400, + .hdisplay = 320, + .hsync_start = 320 + 8, + .hsync_end = 320 + 8 + 42, + .htotal = 320 + 8 + 42 + 30, + .vdisplay = 480, + .vsync_start = 480 + 90, + .vsync_end = 480 + 90 + 24, + .vtotal = 480 + 90 + 24 + 6, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + }, + { /* 50 Hz */ + .clock = 12000, + .hdisplay = 320, + .hsync_start = 320 + 8, + .hsync_end = 320 + 8 + 42, + .htotal = 320 + 8 + 42 + 30, + .vdisplay = 480, + .vsync_start = 480 + 90, + .vsync_end = 480 + 90 + 24, + .vtotal = 480 + 90 + 24 + 6, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + }, +}; + +static const struct a030jtn01_info a030jtn01_info = { + .display_modes = a030jtn01_modes, + .num_modes = ARRAY_SIZE(a030jtn01_modes), + .width_mm = 70, + .height_mm = 51, + .bus_format = MEDIA_BUS_FMT_RGB888_3X8_DELTA, + .bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, +}; + +static const struct spi_device_id a030jtn01_id[] = { + { "a030jtn01", (kernel_ulong_t) &a030jtn01_info }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, a030jtn01_id); + +static const struct of_device_id a030jtn01_of_match[] = { + { .compatible = "auo,a030jtn01" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, a030jtn01_of_match); + +static struct spi_driver a030jtn01_driver = { + .driver = { + .name = "auo-a030jtn01", + .of_match_table = a030jtn01_of_match, + }, + .id_table = a030jtn01_id, + .probe = a030jtn01_probe, + .remove = a030jtn01_remove, +}; +module_spi_driver(a030jtn01_driver); + +MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); +MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c index 5c621b15e84c..439ef3073512 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c @@ -692,7 +692,9 @@ static int s6e3ha2_probe(struct mipi_dsi_device *dsi) dsi->lanes = 4; dsi->format = MIPI_DSI_FMT_RGB888; - dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS; + dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS | + MIPI_DSI_MODE_VIDEO_NO_HFP | MIPI_DSI_MODE_VIDEO_NO_HBP | + MIPI_DSI_MODE_VIDEO_NO_HSA | MIPI_DSI_MODE_NO_EOT_PACKET; ctx->supplies[0].supply = "vdd3"; ctx->supplies[1].supply = "vci"; diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c index e06fd35de814..9c3e76171759 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c @@ -446,7 +446,8 @@ static int s6e63j0x03_probe(struct mipi_dsi_device *dsi) dsi->lanes = 1; dsi->format = MIPI_DSI_FMT_RGB888; - dsi->mode_flags = MIPI_DSI_MODE_NO_EOT_PACKET; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO_NO_HFP | + MIPI_DSI_MODE_VIDEO_NO_HBP | MIPI_DSI_MODE_VIDEO_NO_HSA; ctx->supplies[0].supply = "vdd3"; ctx->supplies[1].supply = "vci"; diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c b/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c index 54213beafaf5..ebf4c2d39ea8 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c @@ -990,8 +990,6 @@ static int s6e8aa0_probe(struct mipi_dsi_device *dsi) dsi->lanes = 4; dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST - | MIPI_DSI_MODE_VIDEO_NO_HFP | MIPI_DSI_MODE_VIDEO_NO_HBP - | MIPI_DSI_MODE_VIDEO_NO_HSA | MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_VSYNC_FLUSH | MIPI_DSI_MODE_VIDEO_AUTO_VERT; ret = s6e8aa0_parse_dt(ctx); diff --git a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c index 1092075b31a5..bb0dfd86ea67 100644 --- a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c +++ b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c @@ -243,13 +243,8 @@ static int visionox_vtdr6130_bl_update_status(struct backlight_device *bl) { struct mipi_dsi_device *dsi = bl_get_data(bl); u16 brightness = backlight_get_brightness(bl); - int ret; - mipi_dsi_dcs_set_display_brightness_large(dsi, brightness); - if (ret < 0) - return ret; - - return 0; + return mipi_dsi_dcs_set_display_brightness_large(dsi, brightness); } static const struct backlight_ops visionox_vtdr6130_bl_ops = { diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index c3bf3a18302e..b16330a8b624 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -81,7 +81,7 @@ #define SSD130X_SET_PRECHARGE_PERIOD2_MASK GENMASK(7, 4) #define SSD130X_SET_PRECHARGE_PERIOD2_SET(val) FIELD_PREP(SSD130X_SET_PRECHARGE_PERIOD2_MASK, (val)) #define SSD130X_SET_COM_PINS_CONFIG1_MASK GENMASK(4, 4) -#define SSD130X_SET_COM_PINS_CONFIG1_SET(val) FIELD_PREP(SSD130X_SET_COM_PINS_CONFIG1_MASK, !(val)) +#define SSD130X_SET_COM_PINS_CONFIG1_SET(val) FIELD_PREP(SSD130X_SET_COM_PINS_CONFIG1_MASK, (val)) #define SSD130X_SET_COM_PINS_CONFIG2_MASK GENMASK(5, 5) #define SSD130X_SET_COM_PINS_CONFIG2_SET(val) FIELD_PREP(SSD130X_SET_COM_PINS_CONFIG2_MASK, (val)) @@ -298,6 +298,7 @@ static void ssd130x_power_off(struct ssd130x_device *ssd130x) static int ssd130x_init(struct ssd130x_device *ssd130x) { u32 precharge, dclk, com_invdir, compins, chargepump, seg_remap; + bool scan_mode; int ret; /* Set initial contrast */ @@ -360,7 +361,13 @@ static int ssd130x_init(struct ssd130x_device *ssd130x) /* Set COM pins configuration */ compins = BIT(1); - compins |= (SSD130X_SET_COM_PINS_CONFIG1_SET(ssd130x->com_seq) | + /* + * The COM scan mode field values are the inverse of the boolean DT + * property "solomon,com-seq". The value 0b means scan from COM0 to + * COM[N - 1] while 1b means scan from COM[N - 1] to COM0. + */ + scan_mode = !ssd130x->com_seq; + compins |= (SSD130X_SET_COM_PINS_CONFIG1_SET(scan_mode) | SSD130X_SET_COM_PINS_CONFIG2_SET(ssd130x->com_lrremap)); ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_COM_PINS_CONFIG, compins); if (ret < 0) diff --git a/drivers/gpu/drm/tiny/simpledrm.c b/drivers/gpu/drm/tiny/simpledrm.c index f658b99c796a..2acc0eb32489 100644 --- a/drivers/gpu/drm/tiny/simpledrm.c +++ b/drivers/gpu/drm/tiny/simpledrm.c @@ -3,6 +3,7 @@ #include <linux/clk.h> #include <linux/of_clk.h> #include <linux/minmax.h> +#include <linux/of_address.h> #include <linux/platform_data/simplefb.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> @@ -184,6 +185,31 @@ simplefb_get_format_of(struct drm_device *dev, struct device_node *of_node) return simplefb_get_validated_format(dev, format); } +static struct resource * +simplefb_get_memory_of(struct drm_device *dev, struct device_node *of_node) +{ + struct device_node *np; + struct resource *res; + int err; + + np = of_parse_phandle(of_node, "memory-region", 0); + if (!np) + return NULL; + + res = devm_kzalloc(dev->dev, sizeof(*res), GFP_KERNEL); + if (!res) + return ERR_PTR(-ENOMEM); + + err = of_address_to_resource(np, 0, res); + if (err) + return ERR_PTR(err); + + if (of_get_property(of_node, "reg", NULL)) + drm_warn(dev, "preferring \"memory-region\" over \"reg\" property\n"); + + return res; +} + /* * Simple Framebuffer device */ @@ -208,7 +234,7 @@ struct simpledrm_device { unsigned int pitch; /* memory management */ - void __iomem *screen_base; + struct iosys_map screen_base; /* modesetting */ uint32_t formats[8]; @@ -473,15 +499,15 @@ static void simpledrm_primary_plane_helper_atomic_update(struct drm_plane *plane drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); drm_atomic_for_each_plane_damage(&iter, &damage) { - struct iosys_map dst = IOSYS_MAP_INIT_VADDR(sdev->screen_base); struct drm_rect dst_clip = plane_state->dst; + struct iosys_map dst = sdev->screen_base; if (!drm_rect_intersect(&dst_clip, &damage)) continue; iosys_map_incr(&dst, drm_fb_clip_offset(sdev->pitch, sdev->format, &dst_clip)); - drm_fb_blit(&dst, &sdev->pitch, sdev->format->format, shadow_plane_state->data, fb, - &damage); + drm_fb_blit(&dst, &sdev->pitch, sdev->format->format, shadow_plane_state->data, + fb, &damage); } drm_dev_exit(idx); @@ -500,7 +526,7 @@ static void simpledrm_primary_plane_helper_atomic_disable(struct drm_plane *plan return; /* Clear screen to black if disabled */ - memset_io(sdev->screen_base, 0, sdev->pitch * sdev->mode.vdisplay); + iosys_map_memset(&sdev->screen_base, 0, 0, sdev->pitch * sdev->mode.vdisplay); drm_dev_exit(idx); } @@ -604,8 +630,7 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, struct drm_device *dev; int width, height, stride; const struct drm_format_info *format; - struct resource *res, *mem; - void __iomem *screen_base; + struct resource *res, *mem = NULL; struct drm_plane *primary_plane; struct drm_crtc *crtc; struct drm_encoder *encoder; @@ -657,6 +682,9 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, format = simplefb_get_format_of(dev, of_node); if (IS_ERR(format)) return ERR_CAST(format); + mem = simplefb_get_memory_of(dev, of_node); + if (IS_ERR(mem)) + return ERR_CAST(mem); } else { drm_err(dev, "no simplefb configuration found\n"); return ERR_PTR(-ENODEV); @@ -679,31 +707,55 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, * Memory management */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return ERR_PTR(-EINVAL); + if (mem) { + void *screen_base; - ret = devm_aperture_acquire_from_firmware(dev, res->start, resource_size(res)); - if (ret) { - drm_err(dev, "could not acquire memory range %pr: error %d\n", res, ret); - return ERR_PTR(ret); - } + ret = devm_aperture_acquire_from_firmware(dev, mem->start, resource_size(mem)); + if (ret) { + drm_err(dev, "could not acquire memory range %pr: %d\n", mem, ret); + return ERR_PTR(ret); + } - mem = devm_request_mem_region(&pdev->dev, res->start, resource_size(res), drv->name); - if (!mem) { - /* - * We cannot make this fatal. Sometimes this comes from magic - * spaces our resource handlers simply don't know about. Use - * the I/O-memory resource as-is and try to map that instead. - */ - drm_warn(dev, "could not acquire memory region %pr\n", res); - mem = res; - } + drm_dbg(dev, "using system memory framebuffer at %pr\n", mem); - screen_base = devm_ioremap_wc(&pdev->dev, mem->start, resource_size(mem)); - if (!screen_base) - return ERR_PTR(-ENOMEM); - sdev->screen_base = screen_base; + screen_base = devm_memremap(dev->dev, mem->start, resource_size(mem), MEMREMAP_WC); + if (!screen_base) + return ERR_PTR(-ENOMEM); + + iosys_map_set_vaddr(&sdev->screen_base, screen_base); + } else { + void __iomem *screen_base; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return ERR_PTR(-EINVAL); + + ret = devm_aperture_acquire_from_firmware(dev, res->start, resource_size(res)); + if (ret) { + drm_err(dev, "could not acquire memory range %pr: %d\n", &res, ret); + return ERR_PTR(ret); + } + + drm_dbg(dev, "using I/O memory framebuffer at %pr\n", res); + + mem = devm_request_mem_region(&pdev->dev, res->start, resource_size(res), + drv->name); + if (!mem) { + /* + * We cannot make this fatal. Sometimes this comes from magic + * spaces our resource handlers simply don't know about. Use + * the I/O-memory resource as-is and try to map that instead. + */ + drm_warn(dev, "could not acquire memory region %pr\n", res); + mem = res; + } + + screen_base = devm_ioremap_wc(&pdev->dev, mem->start, resource_size(mem)); + if (!screen_base) + return ERR_PTR(-ENOMEM); + + iosys_map_set_vaddr_iomem(&sdev->screen_base, screen_base); + } /* * Modesetting diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index 2a71321b9222..a5c075f802e4 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -1819,7 +1819,9 @@ static int vc4_dsi_dev_probe(struct platform_device *pdev) dsi->pdev = pdev; dsi->bridge.funcs = &vc4_dsi_bridge_funcs; +#ifdef CONFIG_OF dsi->bridge.of_node = dev->of_node; +#endif dsi->bridge.type = DRM_MODE_CONNECTOR_DSI; dsi->dsi_host.ops = &vc4_dsi_host_ops; dsi->dsi_host.dev = dev; |