diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-25 02:19:43 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-03-25 02:19:43 +0300 |
commit | b14ffae378aa1db993e62b01392e70d1e585fb23 (patch) | |
tree | 0ac179d24e8a62ec4c2732ed18d90d83da4b82d7 /drivers/gpu/drm/panel | |
parent | 52deda9551a01879b3562e7b41748e85c591f14c (diff) | |
parent | c6e90a1c660874736bd09c1fec6312b4b4c2ff7b (diff) | |
download | linux-b14ffae378aa1db993e62b01392e70d1e585fb23.tar.xz |
Merge tag 'drm-next-2022-03-24' of git://anongit.freedesktop.org/drm/drm
Pull drm updates from Dave Airlie:
"Lots of work all over, Intel improving DG2 support, amdkfd CRIU
support, msm new hw support, and faster fbdev support.
dma-buf:
- rename dma-buf-map to iosys-map
core:
- move buddy allocator to core
- add pci/platform init macros
- improve EDID parser deep color handling
- EDID timing type 7 support
- add GPD Win Max quirk
- add yes/no helpers to string_helpers
- flatten syncobj chains
- add nomodeset support to lots of drivers
- improve fb-helper clipping support
- add default property value interface
fbdev:
- improve fbdev ops speed
ttm:
- add a backpointer from ttm bo->ttm resource
dp:
- move displayport headers
- add a dp helper module
bridge:
- anx7625 atomic support, HDCP support
panel:
- split out panel-lvds and lvds bindings
- find panels in OF subnodes
privacy:
- add chromeos privacy screen support
fb:
- hot unplug fw fb on forced removal
simpledrm:
- request region instead of marking ioresource busy
- add panel oreintation property
udmabuf:
- fix oops with 0 pages
amdgpu:
- power management code cleanup
- Enable freesync video mode by default
- RAS code cleanup
- Improve VRAM access for debug using SDMA
- SR-IOV rework special register access and fixes
- profiling power state request ioctl
- expose IP discovery via sysfs
- Cyan skillfish updates
- GC 10.3.7, SDMA 5.2.7, DCN 3.1.6 updates
- expose benchmark tests via debugfs
- add module param to disable XGMI for testing
- GPU reset debugfs register dumping support
amdkfd:
- CRIU support
- SDMA queue fixes
radeon:
- UVD suspend fix
- iMac backlight fix
i915:
- minimal parallel submission for execlists
- DG2-G12 subplatform added
- DG2 programming workarounds
- DG2 accelerated migration support
- flat CCS and CCS engine support for XeHP
- initial small BAR support
- drop fake LMEM support
- ADL-N PCH support
- bigjoiner updates
- introduce VMA resources and async unbinding
- register definitions cleanups
- multi-FBC refactoring
- DG1 OPROM over SPI support
- ADL-N platform enabling
- opregion mailbox #5 support
- DP MST ESI improvements
- drm device based logging
- async flip optimisation for DG2
- CPU arch abstraction fixes
- improve GuC ADS init to work on aarch64
- tweak TTM LRU priority hint
- GuC 69.0.3 support
- remove short term execbuf pins
nouveau:
- higher DP/eDP bitrates
- backlight fixes
msm:
- dpu + dp support for sc8180x
- dp support for sm8350
- dpu + dsi support for qcm2290
- 10nm dsi phy tuning support
- bridge support for dp encoder
- gpu support for additional 7c3 SKUs
ingenic:
- HDMI support for JZ4780
- aux channel EDID support
ast:
- AST2600 support
- add wide screen support
- create DP/DVI connectors
omapdrm:
- fix implicit dma_buf fencing
vc4:
- add CSC + full range support
- better display firmware handoff
panfrost:
- add initial dual-core GPU support
stm:
- new revision support
- fb handover support
mediatek:
- transfer display binding document to yaml format.
- add mt8195 display device binding.
- allow commands to be sent during video mode.
- add wait_for_event for crtc disable by cmdq.
tegra:
- YUV format support
rcar-du:
- LVDS support for M3-W+ (R8A77961)
exynos:
- BGR pixel format for FIMD device"
* tag 'drm-next-2022-03-24' of git://anongit.freedesktop.org/drm/drm: (1529 commits)
drm/i915/display: Do not re-enable PSR after it was marked as not reliable
drm/i915/display: Fix HPD short pulse handling for eDP
drm/amdgpu: Use drm_mode_copy()
drm/radeon: Use drm_mode_copy()
drm/amdgpu: Use ternary operator in `vcn_v1_0_start()`
drm/amdgpu: Remove pointless on stack mode copies
drm/amd/pm: fix indenting in __smu_cmn_reg_print_error()
drm/amdgpu/dc: fix typos in comments
drm/amdgpu: fix typos in comments
drm/amd/pm: fix typos in comments
drm/amdgpu: Add stolen reserved memory for MI25 SRIOV.
drm/amdgpu: Merge get_reserved_allocation to get_vbios_allocations.
drm/amdkfd: evict svm bo worker handle error
drm/amdgpu/vcn: fix vcn ring test failure in igt reload test
drm/amdgpu: only allow secure submission on rings which support that
drm/amdgpu: fixed the warnings reported by kernel test robot
drm/amd/display: 3.2.177
drm/amd/display: [FW Promotion] Release 0.0.108.0
drm/amd/display: Add save/restore PANEL_PWRSEQ_REF_DIV2
drm/amd/display: Wait for hubp read line for Pollock
...
Diffstat (limited to 'drivers/gpu/drm/panel')
-rw-r--r-- | drivers/gpu/drm/panel/Kconfig | 23 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-edp.c | 102 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-novatek-nt35560.c | 561 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-samsung-atna33xc20.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-simple.c | 34 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-sony-acx424akp.c | 490 |
8 files changed, 709 insertions, 515 deletions
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 9989a316fe88..ddf5f38e8731 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -294,6 +294,18 @@ config DRM_PANEL_NOVATEK_NT35510 around the Novatek NT35510 display controller, such as some Hydis panels. +config DRM_PANEL_NOVATEK_NT35560 + tristate "Novatek NT35560 DSI command mode panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + select VIDEOMODE_HELPERS + help + Say Y here if you want to enable the Novatek NT35560 display + controller. This panel supports DSI in both command and video + mode. This supports several panels such as Sony ACX424AKM and + ACX424AKP. + config DRM_PANEL_NOVATEK_NT35950 tristate "Novatek NT35950 DSI panel" depends on OF @@ -594,17 +606,6 @@ config DRM_PANEL_SITRONIX_ST7789V Say Y here if you want to enable support for the Sitronix ST7789V controller for 240x320 LCD panels -config DRM_PANEL_SONY_ACX424AKP - tristate "Sony ACX424AKP DSI command mode panel" - depends on OF - depends on DRM_MIPI_DSI - depends on BACKLIGHT_CLASS_DEVICE - select VIDEOMODE_HELPERS - help - Say Y here if you want to enable the Sony ACX424 display - panel. This panel supports DSI in both command and video - mode. - config DRM_PANEL_SONY_ACX565AKM tristate "Sony ACX565AKM panel" depends on GPIOLIB && OF && SPI diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index d99fbbce49d1..5740911f637c 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o obj-$(CONFIG_DRM_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35510) += panel-novatek-nt35510.o +obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35560) += panel-novatek-nt35560.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35950) += panel-novatek-nt35950.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672A) += panel-novatek-nt36672a.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o @@ -60,7 +61,6 @@ obj-$(CONFIG_DRM_PANEL_SHARP_LS060T1SX01) += panel-sharp-ls060t1sx01.o obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o -obj-$(CONFIG_DRM_PANEL_SONY_ACX424AKP) += panel-sony-acx424akp.o obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += panel-sony-tulip-truly-nt35521.o obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o diff --git a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c index 5fcbde789ddb..1be150ac758f 100644 --- a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c +++ b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c @@ -86,7 +86,7 @@ static const struct panel_init_cmd boe_tv110c9m_init_cmd[] = { _INIT_DCS_CMD(0x0F, 0x73), _INIT_DCS_CMD(0x95, 0xE6), _INIT_DCS_CMD(0x96, 0xF0), - _INIT_DCS_CMD(0x30, 0x11), + _INIT_DCS_CMD(0x30, 0x00), _INIT_DCS_CMD(0x6D, 0x66), _INIT_DCS_CMD(0x75, 0xA2), _INIT_DCS_CMD(0x77, 0x3B), @@ -112,17 +112,17 @@ static const struct panel_init_cmd boe_tv110c9m_init_cmd[] = { _INIT_DCS_CMD(0xB1, 0x00, 0xD2, 0x01, 0x0B, 0x01, 0x34, 0x01, 0x76, 0x01, 0xA3, 0x01, 0xEF, 0x02, 0x27, 0x02, 0x29), _INIT_DCS_CMD(0xB2, 0x02, 0x5F, 0x02, 0x9E, 0x02, 0xC9, 0x03, 0x00, 0x03, 0x26, 0x03, 0x53, 0x03, 0x63, 0x03, 0x73), - _INIT_DCS_CMD(0xB3, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xA7, 0x03, 0xCF, 0x03, 0xDE, 0x03, 0xE0), + _INIT_DCS_CMD(0xB3, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xAF, 0x03, 0xDF, 0x03, 0xF5, 0x03, 0xE0), _INIT_DCS_CMD(0xB4, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x45, 0x00, 0x65, 0x00, 0x81, 0x00, 0x99, 0x00, 0xAE, 0x00, 0xC1), _INIT_DCS_CMD(0xB5, 0x00, 0xD2, 0x01, 0x0B, 0x01, 0x34, 0x01, 0x76, 0x01, 0xA3, 0x01, 0xEF, 0x02, 0x27, 0x02, 0x29), _INIT_DCS_CMD(0xB6, 0x02, 0x5F, 0x02, 0x9E, 0x02, 0xC9, 0x03, 0x00, 0x03, 0x26, 0x03, 0x53, 0x03, 0x63, 0x03, 0x73), - _INIT_DCS_CMD(0xB7, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xA7, 0x03, 0xCF, 0x03, 0xDE, 0x03, 0xE0), + _INIT_DCS_CMD(0xB7, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xAF, 0x03, 0xDF, 0x03, 0xF5, 0x03, 0xE0), _INIT_DCS_CMD(0xB8, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x45, 0x00, 0x65, 0x00, 0x81, 0x00, 0x99, 0x00, 0xAE, 0x00, 0xC1), _INIT_DCS_CMD(0xB9, 0x00, 0xD2, 0x01, 0x0B, 0x01, 0x34, 0x01, 0x76, 0x01, 0xA3, 0x01, 0xEF, 0x02, 0x27, 0x02, 0x29), _INIT_DCS_CMD(0xBA, 0x02, 0x5F, 0x02, 0x9E, 0x02, 0xC9, 0x03, 0x00, 0x03, 0x26, 0x03, 0x53, 0x03, 0x63, 0x03, 0x73), - _INIT_DCS_CMD(0xBB, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xA7, 0x03, 0xCF, 0x03, 0xDE, 0x03, 0xE0), + _INIT_DCS_CMD(0xBB, 0x03, 0x86, 0x03, 0x9A, 0x03, 0xAF, 0x03, 0xDF, 0x03, 0xF5, 0x03, 0xE0), _INIT_DCS_CMD(0xFF, 0x24), _INIT_DCS_CMD(0xFB, 0x01), diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 176ef0c3cc1d..f7bfcf63d48e 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -21,6 +21,7 @@ * DEALINGS IN THE SOFTWARE. */ +#include <linux/debugfs.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/iopoll.h> @@ -36,8 +37,8 @@ #include <drm/drm_crtc.h> #include <drm/drm_device.h> -#include <drm/drm_dp_aux_bus.h> -#include <drm/drm_dp_helper.h> +#include <drm/dp/drm_dp_aux_bus.h> +#include <drm/dp/drm_dp_helper.h> #include <drm/drm_panel.h> /** @@ -222,6 +223,8 @@ struct panel_edp { struct gpio_desc *enable_gpio; struct gpio_desc *hpd_gpio; + const struct edp_panel_entry *detected_panel; + struct edid *edid; struct drm_display_mode override_mode; @@ -606,6 +609,28 @@ static int panel_edp_get_timings(struct drm_panel *panel, return p->desc->num_timings; } +static int detected_panel_show(struct seq_file *s, void *data) +{ + struct drm_panel *panel = s->private; + struct panel_edp *p = to_panel_edp(panel); + + if (IS_ERR(p->detected_panel)) + seq_puts(s, "UNKNOWN\n"); + else if (!p->detected_panel) + seq_puts(s, "HARDCODED\n"); + else + seq_printf(s, "%s\n", p->detected_panel->name); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(detected_panel); + +static void panel_edp_debugfs_init(struct drm_panel *panel, struct dentry *root) +{ + debugfs_create_file("detected_panel", 0600, root, panel, &detected_panel_fops); +} + static const struct drm_panel_funcs panel_edp_funcs = { .disable = panel_edp_disable, .unprepare = panel_edp_unprepare, @@ -613,6 +638,7 @@ static const struct drm_panel_funcs panel_edp_funcs = { .enable = panel_edp_enable, .get_modes = panel_edp_get_modes, .get_timings = panel_edp_get_timings, + .debugfs_init = panel_edp_debugfs_init, }; #define PANEL_EDP_BOUNDS_CHECK(to_check, bounds, field) \ @@ -666,7 +692,6 @@ static const struct edp_panel_entry *find_edp_panel(u32 panel_id); static int generic_edp_panel_probe(struct device *dev, struct panel_edp *panel) { - const struct edp_panel_entry *edp_panel; struct panel_desc *desc; u32 panel_id; char vend[4]; @@ -705,14 +730,14 @@ static int generic_edp_panel_probe(struct device *dev, struct panel_edp *panel) } drm_edid_decode_panel_id(panel_id, vend, &product_id); - edp_panel = find_edp_panel(panel_id); + panel->detected_panel = find_edp_panel(panel_id); /* * We're using non-optimized timings and want it really obvious that * someone needs to add an entry to the table, so we'll do a WARN_ON * splat. */ - if (WARN_ON(!edp_panel)) { + if (WARN_ON(!panel->detected_panel)) { dev_warn(dev, "Unknown panel %s %#06x, using conservative timings\n", vend, product_id); @@ -734,12 +759,14 @@ static int generic_edp_panel_probe(struct device *dev, struct panel_edp *panel) */ desc->delay.unprepare = 2000; desc->delay.enable = 200; + + panel->detected_panel = ERR_PTR(-EINVAL); } else { dev_info(dev, "Detected %s %s (%#06x)\n", - vend, edp_panel->name, product_id); + vend, panel->detected_panel->name, product_id); /* Update the delay; everything else comes from EDID */ - desc->delay = *edp_panel->delay; + desc->delay = *panel->detected_panel->delay; } ret = 0; @@ -1605,6 +1632,47 @@ static const struct panel_desc sharp_lq123p1jx31 = { }, }; +static const struct drm_display_mode sharp_lq140m1jw46_mode[] = { + { + .clock = 346500, + .hdisplay = 1920, + .hsync_start = 1920 + 48, + .hsync_end = 1920 + 48 + 32, + .htotal = 1920 + 48 + 32 + 80, + .vdisplay = 1080, + .vsync_start = 1080 + 3, + .vsync_end = 1080 + 3 + 5, + .vtotal = 1080 + 3 + 5 + 69, + .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, + }, { + .clock = 144370, + .hdisplay = 1920, + .hsync_start = 1920 + 48, + .hsync_end = 1920 + 48 + 32, + .htotal = 1920 + 48 + 32 + 80, + .vdisplay = 1080, + .vsync_start = 1080 + 3, + .vsync_end = 1080 + 3 + 5, + .vtotal = 1080 + 3 + 5 + 69, + .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, + }, +}; + +static const struct panel_desc sharp_lq140m1jw46 = { + .modes = sharp_lq140m1jw46_mode, + .num_modes = ARRAY_SIZE(sharp_lq140m1jw46_mode), + .bpc = 8, + .size = { + .width = 309, + .height = 174, + }, + .delay = { + .hpd_absent = 80, + .enable = 50, + .unprepare = 500, + }, +}; + static const struct drm_display_mode starry_kr122ea0sra_mode = { .clock = 147000, .hdisplay = 1920, @@ -1719,6 +1787,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "sharp,lq123p1jx31", .data = &sharp_lq123p1jx31, }, { + .compatible = "sharp,lq140m1jw46", + .data = &sharp_lq140m1jw46, + }, { .compatible = "starry,kr122ea0sra", .data = &starry_kr122ea0sra, }, { @@ -1745,6 +1816,19 @@ static const struct panel_delay delay_200_500_e50 = { .enable = 50, }; +static const struct panel_delay delay_200_500_e80_d50 = { + .hpd_absent = 200, + .unprepare = 500, + .enable = 80, + .disable = 50, +}; + +static const struct panel_delay delay_100_500_e200 = { + .hpd_absent = 100, + .unprepare = 500, + .enable = 200, +}; + #define EDP_PANEL_ENTRY(vend_chr_0, vend_chr_1, vend_chr_2, product_id, _delay, _name) \ { \ .name = _name, \ @@ -1768,13 +1852,17 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('B', 'O', 'E', 0x07d1, &boe_nv133fhm_n61.delay, "NV133FHM-N61"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x082d, &boe_nv133fhm_n61.delay, "NV133FHM-N62"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x098d, &boe_nv110wtm_n61.delay, "NV110WTM-N61"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a5d, &delay_200_500_e50, "NV116WHM-N45"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x114c, &innolux_n116bca_ea1.delay, "N116BCA-EA1"), EDP_PANEL_ENTRY('K', 'D', 'B', 0x0624, &kingdisplay_kd116n21_30nv_a010.delay, "116N21-30NV-A010"), + EDP_PANEL_ENTRY('K', 'D', 'B', 0x1120, &delay_200_500_e80_d50, "116N29-30NK-C007"), EDP_PANEL_ENTRY('S', 'H', 'P', 0x154c, &delay_200_500_p2e100, "LQ116M1JW10"), + EDP_PANEL_ENTRY('S', 'T', 'A', 0x0100, &delay_100_500_e200, "2081116HHD028001-51D"), + { /* sentinal */ } }; diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35560.c b/drivers/gpu/drm/panel/panel-novatek-nt35560.c new file mode 100644 index 000000000000..1b6042321ea1 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-novatek-nt35560.c @@ -0,0 +1,561 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * MIPI-DSI Novatek NT35560-based panel controller. + * + * Supported panels include: + * Sony ACX424AKM - a 480x854 AMOLED DSI panel + * Sony ACX424AKP - a 480x864 AMOLED DSI panel + * + * Copyright (C) Linaro Ltd. 2019-2021 + * Author: Linus Walleij + * Based on code and know-how from Marcus Lorentzon + * Copyright (C) ST-Ericsson SA 2010 + * Based on code and know-how from Johan Olson and Joakim Wesslen + * Copyright (C) Sony Ericsson Mobile Communications 2010 + */ +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> + +#define NT35560_DCS_READ_ID1 0xDA +#define NT35560_DCS_READ_ID2 0xDB +#define NT35560_DCS_READ_ID3 0xDC +#define NT35560_DCS_SET_MDDI 0xAE + +/* + * Sony seems to use vendor ID 0x81 + */ +#define DISPLAY_SONY_ACX424AKP_ID1 0x8103 +#define DISPLAY_SONY_ACX424AKP_ID2 0x811a +#define DISPLAY_SONY_ACX424AKP_ID3 0x811b +/* + * The fourth ID looks like a bug, vendor IDs begin at 0x80 + * and panel 00 ... seems like default values. + */ +#define DISPLAY_SONY_ACX424AKP_ID4 0x8000 + +struct nt35560_config { + const struct drm_display_mode *vid_mode; + const struct drm_display_mode *cmd_mode; +}; + +struct nt35560 { + const struct nt35560_config *conf; + struct drm_panel panel; + struct device *dev; + struct regulator *supply; + struct gpio_desc *reset_gpio; + bool video_mode; +}; + +static const struct drm_display_mode sony_acx424akp_vid_mode = { + .clock = 27234, + .hdisplay = 480, + .hsync_start = 480 + 15, + .hsync_end = 480 + 15 + 0, + .htotal = 480 + 15 + 0 + 15, + .vdisplay = 864, + .vsync_start = 864 + 14, + .vsync_end = 864 + 14 + 1, + .vtotal = 864 + 14 + 1 + 11, + .width_mm = 48, + .height_mm = 84, + .flags = DRM_MODE_FLAG_PVSYNC, +}; + +/* + * The timings are not very helpful as the display is used in + * command mode using the maximum HS frequency. + */ +static const struct drm_display_mode sony_acx424akp_cmd_mode = { + .clock = 35478, + .hdisplay = 480, + .hsync_start = 480 + 154, + .hsync_end = 480 + 154 + 16, + .htotal = 480 + 154 + 16 + 32, + .vdisplay = 864, + .vsync_start = 864 + 1, + .vsync_end = 864 + 1 + 1, + .vtotal = 864 + 1 + 1 + 1, + /* + * Some desired refresh rate, experiments at the maximum "pixel" + * clock speed (HS clock 420 MHz) yields around 117Hz. + */ + .width_mm = 48, + .height_mm = 84, +}; + +static const struct nt35560_config sony_acx424akp_data = { + .vid_mode = &sony_acx424akp_vid_mode, + .cmd_mode = &sony_acx424akp_cmd_mode, +}; + +static const struct drm_display_mode sony_acx424akm_vid_mode = { + .clock = 27234, + .hdisplay = 480, + .hsync_start = 480 + 15, + .hsync_end = 480 + 15 + 0, + .htotal = 480 + 15 + 0 + 15, + .vdisplay = 854, + .vsync_start = 854 + 14, + .vsync_end = 854 + 14 + 1, + .vtotal = 854 + 14 + 1 + 11, + .width_mm = 46, + .height_mm = 82, + .flags = DRM_MODE_FLAG_PVSYNC, +}; + +/* + * The timings are not very helpful as the display is used in + * command mode using the maximum HS frequency. + */ +static const struct drm_display_mode sony_acx424akm_cmd_mode = { + .clock = 35478, + .hdisplay = 480, + .hsync_start = 480 + 154, + .hsync_end = 480 + 154 + 16, + .htotal = 480 + 154 + 16 + 32, + .vdisplay = 854, + .vsync_start = 854 + 1, + .vsync_end = 854 + 1 + 1, + .vtotal = 854 + 1 + 1 + 1, + .width_mm = 46, + .height_mm = 82, +}; + +static const struct nt35560_config sony_acx424akm_data = { + .vid_mode = &sony_acx424akm_vid_mode, + .cmd_mode = &sony_acx424akm_cmd_mode, +}; + +static inline struct nt35560 *panel_to_nt35560(struct drm_panel *panel) +{ + return container_of(panel, struct nt35560, panel); +} + +#define FOSC 20 /* 20Mhz */ +#define SCALE_FACTOR_NS_DIV_MHZ 1000 + +static int nt35560_set_brightness(struct backlight_device *bl) +{ + struct nt35560 *nt = bl_get_data(bl); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev); + int period_ns = 1023; + int duty_ns = bl->props.brightness; + u8 pwm_ratio; + u8 pwm_div; + u8 par; + int ret; + + if (backlight_is_blank(bl)) { + /* Disable backlight */ + par = 0x00; + ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, + &par, 1); + if (ret) { + dev_err(nt->dev, "failed to disable display backlight (%d)\n", ret); + return ret; + } + return 0; + } + + /* Calculate the PWM duty cycle in n/256's */ + pwm_ratio = max(((duty_ns * 256) / period_ns) - 1, 1); + pwm_div = max(1, + ((FOSC * period_ns) / 256) / + SCALE_FACTOR_NS_DIV_MHZ); + + /* Set up PWM dutycycle ONE byte (differs from the standard) */ + dev_dbg(nt->dev, "calculated duty cycle %02x\n", pwm_ratio); + ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, + &pwm_ratio, 1); + if (ret < 0) { + dev_err(nt->dev, "failed to set display PWM ratio (%d)\n", ret); + return ret; + } + + /* + * Sequence to write PWMDIV: + * address data + * 0xF3 0xAA CMD2 Unlock + * 0x00 0x01 Enter CMD2 page 0 + * 0X7D 0x01 No reload MTP of CMD2 P1 + * 0x22 PWMDIV + * 0x7F 0xAA CMD2 page 1 lock + */ + par = 0xaa; + ret = mipi_dsi_dcs_write(dsi, 0xf3, &par, 1); + if (ret < 0) { + dev_err(nt->dev, "failed to unlock CMD 2 (%d)\n", ret); + return ret; + } + par = 0x01; + ret = mipi_dsi_dcs_write(dsi, 0x00, &par, 1); + if (ret < 0) { + dev_err(nt->dev, "failed to enter page 1 (%d)\n", ret); + return ret; + } + par = 0x01; + ret = mipi_dsi_dcs_write(dsi, 0x7d, &par, 1); + if (ret < 0) { + dev_err(nt->dev, "failed to disable MTP reload (%d)\n", ret); + return ret; + } + ret = mipi_dsi_dcs_write(dsi, 0x22, &pwm_div, 1); + if (ret < 0) { + dev_err(nt->dev, "failed to set PWM divisor (%d)\n", ret); + return ret; + } + par = 0xaa; + ret = mipi_dsi_dcs_write(dsi, 0x7f, &par, 1); + if (ret < 0) { + dev_err(nt->dev, "failed to lock CMD 2 (%d)\n", ret); + return ret; + } + + /* Enable backlight */ + par = 0x24; + ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, + &par, 1); + if (ret < 0) { + dev_err(nt->dev, "failed to enable display backlight (%d)\n", ret); + return ret; + } + + return 0; +} + +static const struct backlight_ops nt35560_bl_ops = { + .update_status = nt35560_set_brightness, +}; + +static const struct backlight_properties nt35560_bl_props = { + .type = BACKLIGHT_RAW, + .brightness = 512, + .max_brightness = 1023, +}; + +static int nt35560_read_id(struct nt35560 *nt) +{ + struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev); + u8 vendor, version, panel; + u16 val; + int ret; + + ret = mipi_dsi_dcs_read(dsi, NT35560_DCS_READ_ID1, &vendor, 1); + if (ret < 0) { + dev_err(nt->dev, "could not vendor ID byte\n"); + return ret; + } + ret = mipi_dsi_dcs_read(dsi, NT35560_DCS_READ_ID2, &version, 1); + if (ret < 0) { + dev_err(nt->dev, "could not read device version byte\n"); + return ret; + } + ret = mipi_dsi_dcs_read(dsi, NT35560_DCS_READ_ID3, &panel, 1); + if (ret < 0) { + dev_err(nt->dev, "could not read panel ID byte\n"); + return ret; + } + + if (vendor == 0x00) { + dev_err(nt->dev, "device vendor ID is zero\n"); + return -ENODEV; + } + + val = (vendor << 8) | panel; + switch (val) { + case DISPLAY_SONY_ACX424AKP_ID1: + case DISPLAY_SONY_ACX424AKP_ID2: + case DISPLAY_SONY_ACX424AKP_ID3: + case DISPLAY_SONY_ACX424AKP_ID4: + dev_info(nt->dev, "MTP vendor: %02x, version: %02x, panel: %02x\n", + vendor, version, panel); + break; + default: + dev_info(nt->dev, "unknown vendor: %02x, version: %02x, panel: %02x\n", + vendor, version, panel); + break; + } + + return 0; +} + +static int nt35560_power_on(struct nt35560 *nt) +{ + int ret; + + ret = regulator_enable(nt->supply); + if (ret) { + dev_err(nt->dev, "failed to enable supply (%d)\n", ret); + return ret; + } + + /* Assert RESET */ + gpiod_set_value_cansleep(nt->reset_gpio, 1); + udelay(20); + /* De-assert RESET */ + gpiod_set_value_cansleep(nt->reset_gpio, 0); + usleep_range(11000, 20000); + + return 0; +} + +static void nt35560_power_off(struct nt35560 *nt) +{ + /* Assert RESET */ + gpiod_set_value_cansleep(nt->reset_gpio, 1); + usleep_range(11000, 20000); + + regulator_disable(nt->supply); +} + +static int nt35560_prepare(struct drm_panel *panel) +{ + struct nt35560 *nt = panel_to_nt35560(panel); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev); + const u8 mddi = 3; + int ret; + + ret = nt35560_power_on(nt); + if (ret) + return ret; + + ret = nt35560_read_id(nt); + if (ret) { + dev_err(nt->dev, "failed to read panel ID (%d)\n", ret); + goto err_power_off; + } + + /* Enabe tearing mode: send TE (tearing effect) at VBLANK */ + ret = mipi_dsi_dcs_set_tear_on(dsi, + MIPI_DSI_DCS_TEAR_MODE_VBLANK); + if (ret) { + dev_err(nt->dev, "failed to enable vblank TE (%d)\n", ret); + goto err_power_off; + } + + /* + * Set MDDI + * + * This presumably deactivates the Qualcomm MDDI interface and + * selects DSI, similar code is found in other drivers such as the + * Sharp LS043T1LE01 which makes us suspect that this panel may be + * using a Novatek NT35565 or similar display driver chip that shares + * this command. Due to the lack of documentation we cannot know for + * sure. + */ + ret = mipi_dsi_dcs_write(dsi, NT35560_DCS_SET_MDDI, + &mddi, sizeof(mddi)); + if (ret < 0) { + dev_err(nt->dev, "failed to set MDDI (%d)\n", ret); + goto err_power_off; + } + + /* Exit sleep mode */ + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret) { + dev_err(nt->dev, "failed to exit sleep mode (%d)\n", ret); + goto err_power_off; + } + msleep(140); + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret) { + dev_err(nt->dev, "failed to turn display on (%d)\n", ret); + goto err_power_off; + } + if (nt->video_mode) { + /* In video mode turn peripheral on */ + ret = mipi_dsi_turn_on_peripheral(dsi); + if (ret) { + dev_err(nt->dev, "failed to turn on peripheral\n"); + goto err_power_off; + } + } + + return 0; + +err_power_off: + nt35560_power_off(nt); + return ret; +} + +static int nt35560_unprepare(struct drm_panel *panel) +{ + struct nt35560 *nt = panel_to_nt35560(panel); + struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev); + int ret; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret) { + dev_err(nt->dev, "failed to turn display off (%d)\n", ret); + return ret; + } + + /* Enter sleep mode */ + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret) { + dev_err(nt->dev, "failed to enter sleep mode (%d)\n", ret); + return ret; + } + msleep(85); + + nt35560_power_off(nt); + + return 0; +} + + +static int nt35560_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct nt35560 *nt = panel_to_nt35560(panel); + const struct nt35560_config *conf = nt->conf; + struct drm_display_mode *mode; + + if (nt->video_mode) + mode = drm_mode_duplicate(connector->dev, + conf->vid_mode); + else + mode = drm_mode_duplicate(connector->dev, + conf->cmd_mode); + if (!mode) { + dev_err(panel->dev, "bad mode or failed to add mode\n"); + return -EINVAL; + } + drm_mode_set_name(mode); + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + + drm_mode_probed_add(connector, mode); + + return 1; /* Number of modes */ +} + +static const struct drm_panel_funcs nt35560_drm_funcs = { + .unprepare = nt35560_unprepare, + .prepare = nt35560_prepare, + .get_modes = nt35560_get_modes, +}; + +static int nt35560_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct nt35560 *nt; + int ret; + + nt = devm_kzalloc(dev, sizeof(struct nt35560), GFP_KERNEL); + if (!nt) + return -ENOMEM; + nt->video_mode = of_property_read_bool(dev->of_node, + "enforce-video-mode"); + + mipi_dsi_set_drvdata(dsi, nt); + nt->dev = dev; + + nt->conf = of_device_get_match_data(dev); + if (!nt->conf) { + dev_err(dev, "missing device configuration\n"); + return -ENODEV; + } + + dsi->lanes = 2; + dsi->format = MIPI_DSI_FMT_RGB888; + /* + * FIXME: these come from the ST-Ericsson vendor driver for the + * HREF520 and seems to reflect limitations in the PLLs on that + * platform, if you have the datasheet, please cross-check the + * actual max rates. + */ + dsi->lp_rate = 19200000; + dsi->hs_rate = 420160000; + + if (nt->video_mode) + /* Burst mode using event for sync */ + dsi->mode_flags = + MIPI_DSI_MODE_VIDEO | + MIPI_DSI_MODE_VIDEO_BURST; + else + dsi->mode_flags = + MIPI_DSI_CLOCK_NON_CONTINUOUS; + + nt->supply = devm_regulator_get(dev, "vddi"); + if (IS_ERR(nt->supply)) + return PTR_ERR(nt->supply); + + /* This asserts RESET by default */ + nt->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(nt->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(nt->reset_gpio), + "failed to request GPIO\n"); + + drm_panel_init(&nt->panel, dev, &nt35560_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + + nt->panel.backlight = devm_backlight_device_register(dev, "nt35560", dev, nt, + &nt35560_bl_ops, &nt35560_bl_props); + if (IS_ERR(nt->panel.backlight)) + return dev_err_probe(dev, PTR_ERR(nt->panel.backlight), + "failed to register backlight device\n"); + + drm_panel_add(&nt->panel); + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + drm_panel_remove(&nt->panel); + return ret; + } + + return 0; +} + +static int nt35560_remove(struct mipi_dsi_device *dsi) +{ + struct nt35560 *nt = mipi_dsi_get_drvdata(dsi); + + mipi_dsi_detach(dsi); + drm_panel_remove(&nt->panel); + + return 0; +} + +static const struct of_device_id nt35560_of_match[] = { + { + .compatible = "sony,acx424akp", + .data = &sony_acx424akp_data, + }, + { + .compatible = "sony,acx424akm", + .data = &sony_acx424akm_data, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, nt35560_of_match); + +static struct mipi_dsi_driver nt35560_driver = { + .probe = nt35560_probe, + .remove = nt35560_remove, + .driver = { + .name = "panel-novatek-nt35560", + .of_match_table = nt35560_of_match, + }, +}; +module_mipi_dsi_driver(nt35560_driver); + +MODULE_AUTHOR("Linus Wallei <linus.walleij@linaro.org>"); +MODULE_DESCRIPTION("MIPI-DSI Novatek NT35560 Panel Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c b/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c index 221db6512859..20666b6217e7 100644 --- a/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c +++ b/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c @@ -14,8 +14,8 @@ #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> -#include <drm/drm_dp_aux_bus.h> -#include <drm/drm_dp_helper.h> +#include <drm/dp/drm_dp_aux_bus.h> +#include <drm/dp/drm_dp_helper.h> #include <drm/drm_edid.h> #include <drm/drm_panel.h> diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index b42c1d816e79..a34f4198a534 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -2526,6 +2526,36 @@ static const struct panel_desc mitsubishi_aa070mc01 = { .bus_flags = DRM_BUS_FLAG_DE_HIGH, }; +static const struct display_timing multi_inno_mi0700s4t_6_timing = { + .pixelclock = { 29000000, 33000000, 38000000 }, + .hactive = { 800, 800, 800 }, + .hfront_porch = { 180, 210, 240 }, + .hback_porch = { 16, 16, 16 }, + .hsync_len = { 30, 30, 30 }, + .vactive = { 480, 480, 480 }, + .vfront_porch = { 12, 22, 32 }, + .vback_porch = { 10, 10, 10 }, + .vsync_len = { 13, 13, 13 }, + .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | + DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_POSEDGE | + DISPLAY_FLAGS_SYNC_POSEDGE, +}; + +static const struct panel_desc multi_inno_mi0700s4t_6 = { + .timings = &multi_inno_mi0700s4t_6_timing, + .num_timings = 1, + .bpc = 8, + .size = { + .width = 154, + .height = 86, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE | + DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE, + .connector_type = DRM_MODE_CONNECTOR_DPI, +}; + static const struct display_timing multi_inno_mi1010ait_1cp_timing = { .pixelclock = { 68900000, 70000000, 73400000 }, .hactive = { 1280, 1280, 1280 }, @@ -3028,6 +3058,7 @@ static const struct drm_display_mode rocktech_rk101ii01d_ct_mode = { static const struct panel_desc rocktech_rk101ii01d_ct = { .modes = &rocktech_rk101ii01d_ct_mode, + .bpc = 8, .num_modes = 1, .size = { .width = 217, @@ -3873,6 +3904,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "mitsubishi,aa070mc01-ca1", .data = &mitsubishi_aa070mc01, }, { + .compatible = "multi-inno,mi0700s4t-6", + .data = &multi_inno_mi0700s4t_6, + }, { .compatible = "multi-inno,mi1010ait-1cp", .data = &multi_inno_mi1010ait_1cp, }, { diff --git a/drivers/gpu/drm/panel/panel-sony-acx424akp.c b/drivers/gpu/drm/panel/panel-sony-acx424akp.c deleted file mode 100644 index 9536d56a94a5..000000000000 --- a/drivers/gpu/drm/panel/panel-sony-acx424akp.c +++ /dev/null @@ -1,490 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * MIPI-DSI Sony ACX424AKP panel driver. This is a 480x864 - * AMOLED panel with a command-only DSI interface. - * - * Copyright (C) Linaro Ltd. 2019 - * Author: Linus Walleij - * Based on code and know-how from Marcus Lorentzon - * Copyright (C) ST-Ericsson SA 2010 - */ -#include <linux/backlight.h> -#include <linux/delay.h> -#include <linux/gpio/consumer.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/regulator/consumer.h> - -#include <video/mipi_display.h> - -#include <drm/drm_mipi_dsi.h> -#include <drm/drm_modes.h> -#include <drm/drm_panel.h> - -#define ACX424_DCS_READ_ID1 0xDA -#define ACX424_DCS_READ_ID2 0xDB -#define ACX424_DCS_READ_ID3 0xDC -#define ACX424_DCS_SET_MDDI 0xAE - -/* - * Sony seems to use vendor ID 0x81 - */ -#define DISPLAY_SONY_ACX424AKP_ID1 0x811b -#define DISPLAY_SONY_ACX424AKP_ID2 0x811a -/* - * The third ID looks like a bug, vendor IDs begin at 0x80 - * and panel 00 ... seems like default values. - */ -#define DISPLAY_SONY_ACX424AKP_ID3 0x8000 - -struct acx424akp { - struct drm_panel panel; - struct device *dev; - struct regulator *supply; - struct gpio_desc *reset_gpio; - bool video_mode; -}; - -static const struct drm_display_mode sony_acx424akp_vid_mode = { - .clock = 27234, - .hdisplay = 480, - .hsync_start = 480 + 15, - .hsync_end = 480 + 15 + 0, - .htotal = 480 + 15 + 0 + 15, - .vdisplay = 864, - .vsync_start = 864 + 14, - .vsync_end = 864 + 14 + 1, - .vtotal = 864 + 14 + 1 + 11, - .width_mm = 48, - .height_mm = 84, - .flags = DRM_MODE_FLAG_PVSYNC, -}; - -/* - * The timings are not very helpful as the display is used in - * command mode using the maximum HS frequency. - */ -static const struct drm_display_mode sony_acx424akp_cmd_mode = { - .clock = 35478, - .hdisplay = 480, - .hsync_start = 480 + 154, - .hsync_end = 480 + 154 + 16, - .htotal = 480 + 154 + 16 + 32, - .vdisplay = 864, - .vsync_start = 864 + 1, - .vsync_end = 864 + 1 + 1, - .vtotal = 864 + 1 + 1 + 1, - /* - * Some desired refresh rate, experiments at the maximum "pixel" - * clock speed (HS clock 420 MHz) yields around 117Hz. - */ - .width_mm = 48, - .height_mm = 84, -}; - -static inline struct acx424akp *panel_to_acx424akp(struct drm_panel *panel) -{ - return container_of(panel, struct acx424akp, panel); -} - -#define FOSC 20 /* 20Mhz */ -#define SCALE_FACTOR_NS_DIV_MHZ 1000 - -static int acx424akp_set_brightness(struct backlight_device *bl) -{ - struct acx424akp *acx = bl_get_data(bl); - struct mipi_dsi_device *dsi = to_mipi_dsi_device(acx->dev); - int period_ns = 1023; - int duty_ns = bl->props.brightness; - u8 pwm_ratio; - u8 pwm_div; - u8 par; - int ret; - - if (backlight_is_blank(bl)) { - /* Disable backlight */ - par = 0x00; - ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, - &par, 1); - if (ret) { - dev_err(acx->dev, "failed to disable display backlight (%d)\n", ret); - return ret; - } - return 0; - } - - /* Calculate the PWM duty cycle in n/256's */ - pwm_ratio = max(((duty_ns * 256) / period_ns) - 1, 1); - pwm_div = max(1, - ((FOSC * period_ns) / 256) / - SCALE_FACTOR_NS_DIV_MHZ); - - /* Set up PWM dutycycle ONE byte (differs from the standard) */ - dev_dbg(acx->dev, "calculated duty cycle %02x\n", pwm_ratio); - ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, - &pwm_ratio, 1); - if (ret < 0) { - dev_err(acx->dev, "failed to set display PWM ratio (%d)\n", ret); - return ret; - } - - /* - * Sequence to write PWMDIV: - * address data - * 0xF3 0xAA CMD2 Unlock - * 0x00 0x01 Enter CMD2 page 0 - * 0X7D 0x01 No reload MTP of CMD2 P1 - * 0x22 PWMDIV - * 0x7F 0xAA CMD2 page 1 lock - */ - par = 0xaa; - ret = mipi_dsi_dcs_write(dsi, 0xf3, &par, 1); - if (ret < 0) { - dev_err(acx->dev, "failed to unlock CMD 2 (%d)\n", ret); - return ret; - } - par = 0x01; - ret = mipi_dsi_dcs_write(dsi, 0x00, &par, 1); - if (ret < 0) { - dev_err(acx->dev, "failed to enter page 1 (%d)\n", ret); - return ret; - } - par = 0x01; - ret = mipi_dsi_dcs_write(dsi, 0x7d, &par, 1); - if (ret < 0) { - dev_err(acx->dev, "failed to disable MTP reload (%d)\n", ret); - return ret; - } - ret = mipi_dsi_dcs_write(dsi, 0x22, &pwm_div, 1); - if (ret < 0) { - dev_err(acx->dev, "failed to set PWM divisor (%d)\n", ret); - return ret; - } - par = 0xaa; - ret = mipi_dsi_dcs_write(dsi, 0x7f, &par, 1); - if (ret < 0) { - dev_err(acx->dev, "failed to lock CMD 2 (%d)\n", ret); - return ret; - } - - /* Enable backlight */ - par = 0x24; - ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, - &par, 1); - if (ret < 0) { - dev_err(acx->dev, "failed to enable display backlight (%d)\n", ret); - return ret; - } - - return 0; -} - -static const struct backlight_ops acx424akp_bl_ops = { - .update_status = acx424akp_set_brightness, -}; - -static const struct backlight_properties acx424akp_bl_props = { - .type = BACKLIGHT_RAW, - .brightness = 512, - .max_brightness = 1023, -}; - -static int acx424akp_read_id(struct acx424akp *acx) -{ - struct mipi_dsi_device *dsi = to_mipi_dsi_device(acx->dev); - u8 vendor, version, panel; - u16 val; - int ret; - - ret = mipi_dsi_dcs_read(dsi, ACX424_DCS_READ_ID1, &vendor, 1); - if (ret < 0) { - dev_err(acx->dev, "could not vendor ID byte\n"); - return ret; - } - ret = mipi_dsi_dcs_read(dsi, ACX424_DCS_READ_ID2, &version, 1); - if (ret < 0) { - dev_err(acx->dev, "could not read device version byte\n"); - return ret; - } - ret = mipi_dsi_dcs_read(dsi, ACX424_DCS_READ_ID3, &panel, 1); - if (ret < 0) { - dev_err(acx->dev, "could not read panel ID byte\n"); - return ret; - } - - if (vendor == 0x00) { - dev_err(acx->dev, "device vendor ID is zero\n"); - return -ENODEV; - } - - val = (vendor << 8) | panel; - switch (val) { - case DISPLAY_SONY_ACX424AKP_ID1: - case DISPLAY_SONY_ACX424AKP_ID2: - case DISPLAY_SONY_ACX424AKP_ID3: - dev_info(acx->dev, "MTP vendor: %02x, version: %02x, panel: %02x\n", - vendor, version, panel); - break; - default: - dev_info(acx->dev, "unknown vendor: %02x, version: %02x, panel: %02x\n", - vendor, version, panel); - break; - } - - return 0; -} - -static int acx424akp_power_on(struct acx424akp *acx) -{ - int ret; - - ret = regulator_enable(acx->supply); - if (ret) { - dev_err(acx->dev, "failed to enable supply (%d)\n", ret); - return ret; - } - - /* Assert RESET */ - gpiod_set_value_cansleep(acx->reset_gpio, 1); - udelay(20); - /* De-assert RESET */ - gpiod_set_value_cansleep(acx->reset_gpio, 0); - usleep_range(11000, 20000); - - return 0; -} - -static void acx424akp_power_off(struct acx424akp *acx) -{ - /* Assert RESET */ - gpiod_set_value_cansleep(acx->reset_gpio, 1); - usleep_range(11000, 20000); - - regulator_disable(acx->supply); -} - -static int acx424akp_prepare(struct drm_panel *panel) -{ - struct acx424akp *acx = panel_to_acx424akp(panel); - struct mipi_dsi_device *dsi = to_mipi_dsi_device(acx->dev); - const u8 mddi = 3; - int ret; - - ret = acx424akp_power_on(acx); - if (ret) - return ret; - - ret = acx424akp_read_id(acx); - if (ret) { - dev_err(acx->dev, "failed to read panel ID (%d)\n", ret); - goto err_power_off; - } - - /* Enabe tearing mode: send TE (tearing effect) at VBLANK */ - ret = mipi_dsi_dcs_set_tear_on(dsi, - MIPI_DSI_DCS_TEAR_MODE_VBLANK); - if (ret) { - dev_err(acx->dev, "failed to enable vblank TE (%d)\n", ret); - goto err_power_off; - } - - /* - * Set MDDI - * - * This presumably deactivates the Qualcomm MDDI interface and - * selects DSI, similar code is found in other drivers such as the - * Sharp LS043T1LE01 which makes us suspect that this panel may be - * using a Novatek NT35565 or similar display driver chip that shares - * this command. Due to the lack of documentation we cannot know for - * sure. - */ - ret = mipi_dsi_dcs_write(dsi, ACX424_DCS_SET_MDDI, - &mddi, sizeof(mddi)); - if (ret < 0) { - dev_err(acx->dev, "failed to set MDDI (%d)\n", ret); - goto err_power_off; - } - - /* Exit sleep mode */ - ret = mipi_dsi_dcs_exit_sleep_mode(dsi); - if (ret) { - dev_err(acx->dev, "failed to exit sleep mode (%d)\n", ret); - goto err_power_off; - } - msleep(140); - - ret = mipi_dsi_dcs_set_display_on(dsi); - if (ret) { - dev_err(acx->dev, "failed to turn display on (%d)\n", ret); - goto err_power_off; - } - if (acx->video_mode) { - /* In video mode turn peripheral on */ - ret = mipi_dsi_turn_on_peripheral(dsi); - if (ret) { - dev_err(acx->dev, "failed to turn on peripheral\n"); - goto err_power_off; - } - } - - return 0; - -err_power_off: - acx424akp_power_off(acx); - return ret; -} - -static int acx424akp_unprepare(struct drm_panel *panel) -{ - struct acx424akp *acx = panel_to_acx424akp(panel); - struct mipi_dsi_device *dsi = to_mipi_dsi_device(acx->dev); - int ret; - - ret = mipi_dsi_dcs_set_display_off(dsi); - if (ret) { - dev_err(acx->dev, "failed to turn display off (%d)\n", ret); - return ret; - } - - /* Enter sleep mode */ - ret = mipi_dsi_dcs_enter_sleep_mode(dsi); - if (ret) { - dev_err(acx->dev, "failed to enter sleep mode (%d)\n", ret); - return ret; - } - msleep(85); - - acx424akp_power_off(acx); - - return 0; -} - - -static int acx424akp_get_modes(struct drm_panel *panel, - struct drm_connector *connector) -{ - struct acx424akp *acx = panel_to_acx424akp(panel); - struct drm_display_mode *mode; - - if (acx->video_mode) - mode = drm_mode_duplicate(connector->dev, - &sony_acx424akp_vid_mode); - else - mode = drm_mode_duplicate(connector->dev, - &sony_acx424akp_cmd_mode); - if (!mode) { - dev_err(panel->dev, "bad mode or failed to add mode\n"); - return -EINVAL; - } - drm_mode_set_name(mode); - mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; - - connector->display_info.width_mm = mode->width_mm; - connector->display_info.height_mm = mode->height_mm; - - drm_mode_probed_add(connector, mode); - - return 1; /* Number of modes */ -} - -static const struct drm_panel_funcs acx424akp_drm_funcs = { - .unprepare = acx424akp_unprepare, - .prepare = acx424akp_prepare, - .get_modes = acx424akp_get_modes, -}; - -static int acx424akp_probe(struct mipi_dsi_device *dsi) -{ - struct device *dev = &dsi->dev; - struct acx424akp *acx; - int ret; - - acx = devm_kzalloc(dev, sizeof(struct acx424akp), GFP_KERNEL); - if (!acx) - return -ENOMEM; - acx->video_mode = of_property_read_bool(dev->of_node, - "enforce-video-mode"); - - mipi_dsi_set_drvdata(dsi, acx); - acx->dev = dev; - - dsi->lanes = 2; - dsi->format = MIPI_DSI_FMT_RGB888; - /* - * FIXME: these come from the ST-Ericsson vendor driver for the - * HREF520 and seems to reflect limitations in the PLLs on that - * platform, if you have the datasheet, please cross-check the - * actual max rates. - */ - dsi->lp_rate = 19200000; - dsi->hs_rate = 420160000; - - if (acx->video_mode) - /* Burst mode using event for sync */ - dsi->mode_flags = - MIPI_DSI_MODE_VIDEO | - MIPI_DSI_MODE_VIDEO_BURST; - else - dsi->mode_flags = - MIPI_DSI_CLOCK_NON_CONTINUOUS; - - acx->supply = devm_regulator_get(dev, "vddi"); - if (IS_ERR(acx->supply)) - return PTR_ERR(acx->supply); - - /* This asserts RESET by default */ - acx->reset_gpio = devm_gpiod_get_optional(dev, "reset", - GPIOD_OUT_HIGH); - if (IS_ERR(acx->reset_gpio)) - return dev_err_probe(dev, PTR_ERR(acx->reset_gpio), - "failed to request GPIO\n"); - - drm_panel_init(&acx->panel, dev, &acx424akp_drm_funcs, - DRM_MODE_CONNECTOR_DSI); - - acx->panel.backlight = devm_backlight_device_register(dev, "acx424akp", dev, acx, - &acx424akp_bl_ops, &acx424akp_bl_props); - if (IS_ERR(acx->panel.backlight)) - return dev_err_probe(dev, PTR_ERR(acx->panel.backlight), - "failed to register backlight device\n"); - - drm_panel_add(&acx->panel); - - ret = mipi_dsi_attach(dsi); - if (ret < 0) { - drm_panel_remove(&acx->panel); - return ret; - } - - return 0; -} - -static int acx424akp_remove(struct mipi_dsi_device *dsi) -{ - struct acx424akp *acx = mipi_dsi_get_drvdata(dsi); - - mipi_dsi_detach(dsi); - drm_panel_remove(&acx->panel); - - return 0; -} - -static const struct of_device_id acx424akp_of_match[] = { - { .compatible = "sony,acx424akp" }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, acx424akp_of_match); - -static struct mipi_dsi_driver acx424akp_driver = { - .probe = acx424akp_probe, - .remove = acx424akp_remove, - .driver = { - .name = "panel-sony-acx424akp", - .of_match_table = acx424akp_of_match, - }, -}; -module_mipi_dsi_driver(acx424akp_driver); - -MODULE_AUTHOR("Linus Wallei <linus.walleij@linaro.org>"); -MODULE_DESCRIPTION("MIPI-DSI Sony acx424akp Panel Driver"); -MODULE_LICENSE("GPL v2"); |