Merge tag 'drm-misc-next-2023-03-16' of git://anongit.freedesktop.org/drm/drm-misc into drm-next

drm-misc-next for v6.4-rc1:

Cross-subsystem Changes:
- Add drm_bridge.h to drm_bridge maintainers.

Core Changes:
- Assorted fixes to TTM, tests, format-helper, accel.
- Assorted Makefile fixes to drivers and accel.
- Implement fbdev emulation for GEM DMA drivers, and convert a lot of
  drivers to use it.
- Use tgid instead of pid for tracking clients.

Driver Changes:
- Assorted fixes in rockchip, vmwgfx, nouveau, cirrus.
- Add imx25 driver.
- Add Elida KD50T048A, Sony TD4353, Novatek NT36523, STARRY 2081101QFH032011-53G panels.
- Add 4K mode support to rockchip.
- Convert cirrus to use regular atomic helpers, and more cirrus
  improvements.
- Add damage clipping to cirrus, virtio.

[airlied: add drm_bridge.h include to imx]
Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/f7b765c7-d49d-edb5-2a6a-4f7a7be16a59@linux.intel.com
This commit is contained in:
Dave Airlie 2023-03-21 11:03:16 +10:00
commit c6265f5c2f
74 changed files with 2974 additions and 326 deletions

View File

@ -21,6 +21,9 @@ properties:
- fsl,imx25-fb
- fsl,imx27-fb
- const: fsl,imx21-fb
- items:
- const: fsl,imx25-lcdc
- const: fsl,imx21-lcdc
clocks:
maxItems: 3
@ -31,6 +34,9 @@ properties:
- const: ahb
- const: per
port:
$ref: /schemas/graph.yaml#/properties/port
display:
$ref: /schemas/types.yaml#/definitions/phandle
@ -59,17 +65,55 @@ properties:
description:
LCDC Sharp Configuration Register value.
allOf:
- if:
properties:
compatible:
contains:
enum:
- fsl,imx1-lcdc
- fsl,imx21-lcdc
then:
properties:
display: false
fsl,dmacr: false
fsl,lpccr: false
fsl,lscr1: false
required:
- port
else:
properties:
port: false
required:
- display
required:
- compatible
- clocks
- clock-names
- display
- interrupts
- reg
additionalProperties: false
examples:
- |
lcdc@53fbc000 {
compatible = "fsl,imx25-lcdc", "fsl,imx21-lcdc";
reg = <0x53fbc000 0x4000>;
interrupts = <39>;
clocks = <&clks 103>, <&clks 66>, <&clks 49>;
clock-names = "ipg", "ahb", "per";
port {
parallel_out: endpoint {
remote-endpoint = <&panel_in>;
};
};
};
- |
imxfb: fb@10021000 {
compatible = "fsl,imx21-fb";

View File

@ -30,6 +30,8 @@ properties:
- boe,tv110c9m-ll3
# INX HJ110IZ-01A 10.95" WUXGA TFT LCD panel
- innolux,hj110iz-01a
# STARRY 2081101QFH032011-53G 10.1" WUXGA TFT LCD panel
- starry,2081101qfh032011-53g
reg:
description: the virtual channel number of a DSI peripheral

View File

@ -0,0 +1,85 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/novatek,nt36523.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Novatek NT36523 based DSI display Panels
maintainers:
- Jianhua Lu <lujianhua000@gmail.com>
description: |
The Novatek NT36523 is a generic DSI Panel IC used to drive dsi
panels. Support video mode panels from China Star Optoelectronics
Technology (CSOT) and BOE Technology.
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
items:
- enum:
- xiaomi,elish-boe-nt36523
- xiaomi,elish-csot-nt36523
- const: novatek,nt36523
reset-gpios:
maxItems: 1
description: phandle of gpio for reset line - This should be 8mA
vddio-supply:
description: regulator that supplies the I/O voltage
reg: true
ports: true
backlight: true
required:
- compatible
- reg
- vddio-supply
- reset-gpios
- ports
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "xiaomi,elish-csot-nt36523", "novatek,nt36523";
reg = <0>;
vddio-supply = <&vreg_l14a_1p88>;
reset-gpios = <&tlmm 75 GPIO_ACTIVE_LOW>;
backlight = <&backlight>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
panel_in_0: endpoint {
remote-endpoint = <&dsi0_out>;
};
};
port@1{
reg = <1>;
panel_in_1: endpoint {
remote-endpoint = <&dsi1_out>;
};
};
};
};
};
...

View File

@ -25,6 +25,8 @@ properties:
avdd-supply:
description: 5v analog regulator
enable-gpios: true
required:
- compatible
- dvdd-supply

View File

@ -28,6 +28,7 @@ properties:
items:
- enum:
- densitron,dmt028vghmcmi-1a
- elida,kd50t048a
- techstar,ts8550b
- const: sitronix,st7701

View File

@ -0,0 +1,82 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/sony,td4353-jdi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Sony TD4353 JDI 5 / 5.7" 2160x1080 MIPI-DSI Panel
maintainers:
- Konrad Dybcio <konrad.dybcio@somainline.org>
description: |
The Sony TD4353 JDI is a 5 (XZ2c) / 5.7 (XZ2) inch 2160x1080
MIPI-DSI panel, used in Xperia XZ2 and XZ2 Compact smartphones.
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
const: sony,td4353-jdi-tama
reg: true
backlight: true
vddio-supply:
description: VDDIO 1.8V supply
vsp-supply:
description: Positive 5.5V supply
vsn-supply:
description: Negative 5.5V supply
panel-reset-gpios:
description: Display panel reset pin
touch-reset-gpios:
description: Touch panel reset pin
port: true
required:
- compatible
- reg
- vddio-supply
- vsp-supply
- vsn-supply
- panel-reset-gpios
- touch-reset-gpios
- port
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel: panel@0 {
compatible = "sony,td4353-jdi-tama";
reg = <0>;
backlight = <&pmi8998_wled>;
vddio-supply = <&vreg_l14a_1p8>;
vsp-supply = <&lab>;
vsn-supply = <&ibb>;
panel-reset-gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>;
touch-reset-gpios = <&tlmm 99 GPIO_ACTIVE_HIGH>;
port {
panel_in: endpoint {
remote-endpoint = <&dsi0_out>;
};
};
};
};
...

View File

@ -6537,6 +6537,13 @@ T: git git://anongit.freedesktop.org/drm/drm-misc
F: Documentation/devicetree/bindings/display/panel/sony,acx424akp.yaml
F: drivers/gpu/drm/panel/panel-novatek-nt35560.c
DRM DRIVER FOR NOVATEK NT36523 PANELS
M: Jianhua Lu <lujianhua000@gmail.com>
S: Maintained
T: git git://anongit.freedesktop.org/drm/drm-misc
F: Documentation/devicetree/bindings/display/panel/novatek,nt36523.yaml
F: drivers/gpu/drm/panel/panel-novatek-nt36523.c
DRM DRIVER FOR NOVATEK NT36672A PANELS
M: Sumit Semwal <sumit.semwal@linaro.org>
S: Maintained
@ -6824,6 +6831,7 @@ S: Maintained
T: git git://anongit.freedesktop.org/drm/drm-misc
F: Documentation/devicetree/bindings/display/bridge/
F: drivers/gpu/drm/bridge/
F: include/drm/drm_bridge.h
DRM DRIVERS FOR EXYNOS
M: Inki Dae <inki.dae@samsung.com>

View File

@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += habanalabs/
obj-y += ivpu/
obj-$(CONFIG_DRM_ACCEL_HABANALABS) += habanalabs/
obj-$(CONFIG_DRM_ACCEL_IVPU) += ivpu/

View File

@ -82,6 +82,7 @@ obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o
obj-$(CONFIG_DRM_BUDDY) += drm_buddy.o
drm_dma_helper-y := drm_gem_dma_helper.o
drm_dma_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fbdev_dma.o
drm_dma_helper-$(CONFIG_DRM_KMS_HELPER) += drm_fb_dma_helper.o
obj-$(CONFIG_DRM_GEM_DMA_HELPER) += drm_dma_helper.o

View File

@ -969,7 +969,7 @@ static int amdgpu_debugfs_gem_info_show(struct seq_file *m, void *unused)
* Therefore, we need to protect this ->comm access using RCU.
*/
rcu_read_lock();
task = pid_task(file->pid, PIDTYPE_PID);
task = pid_task(file->pid, PIDTYPE_TGID);
seq_printf(m, "pid %8d command %s:\n", pid_nr(file->pid),
task ? task->comm : "<unknown>");
rcu_read_unlock();

View File

@ -26,7 +26,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_debugfs.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_modeset_helper.h>
@ -301,7 +301,7 @@ static int hdlcd_drm_bind(struct device *dev)
if (ret)
goto err_register;
drm_fbdev_generic_setup(drm, 32);
drm_fbdev_dma_setup(drm, 32);
return 0;

View File

@ -19,7 +19,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
@ -852,7 +852,7 @@ static int malidp_bind(struct device *dev)
if (ret)
goto register_fail;
drm_fbdev_generic_setup(drm, 32);
drm_fbdev_dma_setup(drm, 32);
return 0;

View File

@ -15,7 +15,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_device.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_module.h>
@ -341,7 +341,7 @@ static int aspeed_gfx_probe(struct platform_device *pdev)
if (ret)
goto err_unload;
drm_fbdev_generic_setup(&priv->drm, 32);
drm_fbdev_dma_setup(&priv->drm, 32);
return 0;
err_unload:

View File

@ -19,7 +19,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_module.h>
@ -760,7 +760,7 @@ static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev)
if (ret)
goto err_unload;
drm_fbdev_generic_setup(ddev, 24);
drm_fbdev_dma_setup(ddev, 24);
return 0;

View File

@ -80,7 +80,7 @@ static int drm_clients_info(struct seq_file *m, void *data)
seq_printf(m,
"%20s %5s %3s master a %5s %10s\n",
"command",
"pid",
"tgid",
"dev",
"uid",
"magic");
@ -94,7 +94,7 @@ static int drm_clients_info(struct seq_file *m, void *data)
bool is_current_master = drm_is_current_master(priv);
rcu_read_lock(); /* locks pid_task()->comm */
task = pid_task(priv->pid, PIDTYPE_PID);
task = pid_task(priv->pid, PIDTYPE_TGID);
uid = task ? __task_cred(task)->euid : GLOBAL_ROOT_UID;
seq_printf(m, "%20s %5d %3d %c %c %5d %10u\n",
task ? task->comm : "<unknown>",

View File

@ -0,0 +1,275 @@
// SPDX-License-Identifier: MIT
#include <drm/drm_crtc_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_fbdev_dma.h>
/*
* struct fb_ops
*/
static int drm_fbdev_dma_fb_open(struct fb_info *info, int user)
{
struct drm_fb_helper *fb_helper = info->par;
/* No need to take a ref for fbcon because it unbinds on unregister */
if (user && !try_module_get(fb_helper->dev->driver->fops->owner))
return -ENODEV;
return 0;
}
static int drm_fbdev_dma_fb_release(struct fb_info *info, int user)
{
struct drm_fb_helper *fb_helper = info->par;
if (user)
module_put(fb_helper->dev->driver->fops->owner);
return 0;
}
static void drm_fbdev_dma_fb_destroy(struct fb_info *info)
{
struct drm_fb_helper *fb_helper = info->par;
if (!fb_helper->dev)
return;
drm_fb_helper_fini(fb_helper);
drm_client_buffer_vunmap(fb_helper->buffer);
drm_client_framebuffer_delete(fb_helper->buffer);
drm_client_release(&fb_helper->client);
drm_fb_helper_unprepare(fb_helper);
kfree(fb_helper);
}
static int drm_fbdev_dma_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
struct drm_fb_helper *fb_helper = info->par;
struct drm_device *dev = fb_helper->dev;
if (drm_WARN_ON_ONCE(dev, !fb_helper->dev->driver->gem_prime_mmap))
return -ENODEV;
return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
}
static const struct fb_ops drm_fbdev_dma_fb_ops = {
.owner = THIS_MODULE,
.fb_open = drm_fbdev_dma_fb_open,
.fb_release = drm_fbdev_dma_fb_release,
.fb_read = drm_fb_helper_sys_read,
.fb_write = drm_fb_helper_sys_write,
DRM_FB_HELPER_DEFAULT_OPS,
.fb_fillrect = drm_fb_helper_sys_fillrect,
.fb_copyarea = drm_fb_helper_sys_copyarea,
.fb_imageblit = drm_fb_helper_sys_imageblit,
.fb_destroy = drm_fbdev_dma_fb_destroy,
.fb_mmap = drm_fbdev_dma_fb_mmap,
};
/*
* struct drm_fb_helper
*/
static int drm_fbdev_dma_helper_fb_probe(struct drm_fb_helper *fb_helper,
struct drm_fb_helper_surface_size *sizes)
{
struct drm_client_dev *client = &fb_helper->client;
struct drm_device *dev = fb_helper->dev;
struct drm_client_buffer *buffer;
struct drm_gem_dma_object *dma_obj;
struct drm_framebuffer *fb;
struct fb_info *info;
u32 format;
struct iosys_map map;
int ret;
drm_dbg_kms(dev, "surface width(%d), height(%d) and bpp(%d)\n",
sizes->surface_width, sizes->surface_height,
sizes->surface_bpp);
format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth);
buffer = drm_client_framebuffer_create(client, sizes->surface_width,
sizes->surface_height, format);
if (IS_ERR(buffer))
return PTR_ERR(buffer);
dma_obj = to_drm_gem_dma_obj(buffer->gem);
fb = buffer->fb;
if (drm_WARN_ON(dev, fb->funcs->dirty)) {
ret = -ENODEV; /* damage handling not supported; use generic emulation */
goto err_drm_client_buffer_delete;
}
ret = drm_client_buffer_vmap(buffer, &map);
if (ret) {
goto err_drm_client_buffer_delete;
} else if (drm_WARN_ON(dev, map.is_iomem)) {
ret = -ENODEV; /* I/O memory not supported; use generic emulation */
goto err_drm_client_buffer_delete;
}
fb_helper->buffer = buffer;
fb_helper->fb = buffer->fb;
info = drm_fb_helper_alloc_info(fb_helper);
if (IS_ERR(info)) {
ret = PTR_ERR(info);
goto err_drm_client_buffer_vunmap;
}
drm_fb_helper_fill_info(info, fb_helper, sizes);
info->fbops = &drm_fbdev_dma_fb_ops;
info->flags = FBINFO_DEFAULT;
/* screen */
info->flags |= FBINFO_VIRTFB; /* system memory */
if (dma_obj->map_noncoherent)
info->flags |= FBINFO_READS_FAST; /* signal caching */
info->screen_size = sizes->surface_height * fb->pitches[0];
info->screen_buffer = map.vaddr;
info->fix.smem_len = info->screen_size;
#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM)
/*
* Shamelessly leak the physical address to user-space.
*/
if (fb_helper->hint_leak_smem_start && !info->fix.smem_start)
info->fix.smem_start = page_to_phys(virt_to_page(info->screen_buffer));
#endif
return 0;
err_drm_client_buffer_vunmap:
fb_helper->fb = NULL;
fb_helper->buffer = NULL;
drm_client_buffer_vunmap(buffer);
err_drm_client_buffer_delete:
drm_client_framebuffer_delete(buffer);
return ret;
}
static const struct drm_fb_helper_funcs drm_fbdev_dma_helper_funcs = {
.fb_probe = drm_fbdev_dma_helper_fb_probe,
};
/*
* struct drm_client_funcs
*/
static void drm_fbdev_dma_client_unregister(struct drm_client_dev *client)
{
struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
if (fb_helper->info) {
drm_fb_helper_unregister_info(fb_helper);
} else {
drm_client_release(&fb_helper->client);
drm_fb_helper_unprepare(fb_helper);
kfree(fb_helper);
}
}
static int drm_fbdev_dma_client_restore(struct drm_client_dev *client)
{
drm_fb_helper_lastclose(client->dev);
return 0;
}
static int drm_fbdev_dma_client_hotplug(struct drm_client_dev *client)
{
struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
struct drm_device *dev = client->dev;
int ret;
if (dev->fb_helper)
return drm_fb_helper_hotplug_event(dev->fb_helper);
ret = drm_fb_helper_init(dev, fb_helper);
if (ret)
goto err_drm_err;
if (!drm_drv_uses_atomic_modeset(dev))
drm_helper_disable_unused_functions(dev);
ret = drm_fb_helper_initial_config(fb_helper);
if (ret)
goto err_drm_fb_helper_fini;
return 0;
err_drm_fb_helper_fini:
drm_fb_helper_fini(fb_helper);
err_drm_err:
drm_err(dev, "fbdev-dma: Failed to setup generic emulation (ret=%d)\n", ret);
return ret;
}
static const struct drm_client_funcs drm_fbdev_dma_client_funcs = {
.owner = THIS_MODULE,
.unregister = drm_fbdev_dma_client_unregister,
.restore = drm_fbdev_dma_client_restore,
.hotplug = drm_fbdev_dma_client_hotplug,
};
/**
* drm_fbdev_dma_setup() - Setup fbdev emulation for GEM DMA helpers
* @dev: DRM device
* @preferred_bpp: Preferred bits per pixel for the device.
* @dev->mode_config.preferred_depth is used if this is zero.
*
* This function sets up fbdev emulation for GEM DMA drivers that support
* dumb buffers with a virtual address and that can be mmap'ed.
* drm_fbdev_dma_setup() shall be called after the DRM driver registered
* the new DRM device with drm_dev_register().
*
* Restore, hotplug events and teardown are all taken care of. Drivers that do
* suspend/resume need to call drm_fb_helper_set_suspend_unlocked() themselves.
* Simple drivers might use drm_mode_config_helper_suspend().
*
* This function is safe to call even when there are no connectors present.
* Setup will be retried on the next hotplug event.
*
* The fbdev is destroyed by drm_dev_unregister().
*/
void drm_fbdev_dma_setup(struct drm_device *dev, unsigned int preferred_bpp)
{
struct drm_fb_helper *fb_helper;
int ret;
drm_WARN(dev, !dev->registered, "Device has not been registered.\n");
drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n");
fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
if (!fb_helper)
return;
drm_fb_helper_prepare(dev, fb_helper, preferred_bpp, &drm_fbdev_dma_helper_funcs);
ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_dma_client_funcs);
if (ret) {
drm_err(dev, "Failed to register client: %d\n", ret);
goto err_drm_client_init;
}
ret = drm_fbdev_dma_client_hotplug(&fb_helper->client);
if (ret)
drm_dbg_kms(dev, "client hotplug ret=%d\n", ret);
drm_client_register(&fb_helper->client);
return;
err_drm_client_init:
drm_fb_helper_unprepare(fb_helper);
kfree(fb_helper);
}
EXPORT_SYMBOL(drm_fbdev_dma_setup);

View File

@ -156,7 +156,7 @@ struct drm_file *drm_file_alloc(struct drm_minor *minor)
if (!file)
return ERR_PTR(-ENOMEM);
file->pid = get_pid(task_pid(current));
file->pid = get_pid(task_tgid(current));
file->minor = minor;
/* for compatibility root is always authenticated */

View File

@ -2,7 +2,7 @@
config DRM_EXYNOS
tristate "DRM Support for Samsung SoC Exynos Series"
depends on OF && DRM && COMMON_CLK
depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS || ARCH_MULTIPLATFORM || COMPILE_TEST
depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
depends on MMU
select DRM_DISPLAY_HELPER if DRM_EXYNOS_DP
select DRM_KMS_HELPER

View File

@ -20,7 +20,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_modeset_helper.h>
#include <drm/drm_module.h>
@ -333,7 +333,7 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
if (ret < 0)
goto put;
drm_fbdev_generic_setup(drm, legacyfb_depth);
drm_fbdev_dma_setup(drm, legacyfb_depth);
return 0;

View File

@ -2,3 +2,4 @@
source "drivers/gpu/drm/imx/dcss/Kconfig"
source "drivers/gpu/drm/imx/ipuv3/Kconfig"
source "drivers/gpu/drm/imx/lcdc/Kconfig"

View File

@ -2,3 +2,4 @@
obj-$(CONFIG_DRM_IMX_DCSS) += dcss/
obj-$(CONFIG_DRM_IMX) += ipuv3/
obj-$(CONFIG_DRM_IMX_LCDC) += lcdc/

View File

@ -7,7 +7,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_of.h>
@ -145,7 +145,7 @@ struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss)
if (ret)
goto cleanup_crtc;
drm_fbdev_generic_setup(drm, 32);
drm_fbdev_dma_setup(drm, 32);
return kms;

View File

@ -4,7 +4,7 @@ config DRM_IMX
select DRM_KMS_HELPER
select VIDEOMODE_HELPERS
select DRM_GEM_DMA_HELPER
depends on DRM && (ARCH_MXC || ARCH_MULTIPLATFORM || COMPILE_TEST)
depends on DRM && (ARCH_MXC || COMPILE_TEST)
depends on IMX_IPUV3_CORE
help
enable i.MX graphics support

View File

@ -16,7 +16,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_managed.h>
@ -249,7 +249,7 @@ static int imx_drm_bind(struct device *dev)
if (ret)
goto err_poll_fini;
drm_fbdev_generic_setup(drm, legacyfb_depth);
drm_fbdev_dma_setup(drm, legacyfb_depth);
return 0;

View File

@ -0,0 +1,7 @@
config DRM_IMX_LCDC
tristate "Freescale i.MX LCDC displays"
depends on DRM && (ARCH_MXC || COMPILE_TEST)
select DRM_GEM_DMA_HELPER
select DRM_KMS_HELPER
help
Found on i.MX1, i.MX21, i.MX25 and i.MX27.

View File

@ -0,0 +1 @@
obj-$(CONFIG_DRM_IMX_LCDC) += imx-lcdc.o

View File

@ -0,0 +1,546 @@
// SPDX-License-Identifier: GPL-2.0-only
// SPDX-FileCopyrightText: 2020 Marian Cichy <M.Cichy@pengutronix.de>
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#define IMX21LCDC_LSSAR 0x0000 /* LCDC Screen Start Address Register */
#define IMX21LCDC_LSR 0x0004 /* LCDC Size Register */
#define IMX21LCDC_LVPWR 0x0008 /* LCDC Virtual Page Width Register */
#define IMX21LCDC_LCPR 0x000C /* LCDC Cursor Position Register */
#define IMX21LCDC_LCWHB 0x0010 /* LCDC Cursor Width Height and Blink Register*/
#define IMX21LCDC_LCCMR 0x0014 /* LCDC Color Cursor Mapping Register */
#define IMX21LCDC_LPCR 0x0018 /* LCDC Panel Configuration Register */
#define IMX21LCDC_LHCR 0x001C /* LCDC Horizontal Configuration Register */
#define IMX21LCDC_LVCR 0x0020 /* LCDC Vertical Configuration Register */
#define IMX21LCDC_LPOR 0x0024 /* LCDC Panning Offset Register */
#define IMX21LCDC_LSCR 0x0028 /* LCDC Sharp Configuration Register */
#define IMX21LCDC_LPCCR 0x002C /* LCDC PWM Contrast Control Register */
#define IMX21LCDC_LDCR 0x0030 /* LCDC DMA Control Register */
#define IMX21LCDC_LRMCR 0x0034 /* LCDC Refresh Mode Control Register */
#define IMX21LCDC_LICR 0x0038 /* LCDC Interrupt Configuration Register */
#define IMX21LCDC_LIER 0x003C /* LCDC Interrupt Enable Register */
#define IMX21LCDC_LISR 0x0040 /* LCDC Interrupt Status Register */
#define IMX21LCDC_LGWSAR 0x0050 /* LCDC Graphic Window Start Address Register */
#define IMX21LCDC_LGWSR 0x0054 /* LCDC Graph Window Size Register */
#define IMX21LCDC_LGWVPWR 0x0058 /* LCDC Graphic Window Virtual Page Width Register */
#define IMX21LCDC_LGWPOR 0x005C /* LCDC Graphic Window Panning Offset Register */
#define IMX21LCDC_LGWPR 0x0060 /* LCDC Graphic Window Position Register */
#define IMX21LCDC_LGWCR 0x0064 /* LCDC Graphic Window Control Register */
#define IMX21LCDC_LGWDCR 0x0068 /* LCDC Graphic Window DMA Control Register */
#define IMX21LCDC_LAUSCR 0x0080 /* LCDC AUS Mode Control Register */
#define IMX21LCDC_LAUSCCR 0x0084 /* LCDC AUS Mode Cursor Control Register */
#define IMX21LCDC_BGLUT 0x0800 /* Background Lookup Table */
#define IMX21LCDC_GWLUT 0x0C00 /* Graphic Window Lookup Table */
#define IMX21LCDC_LCPR_CC0 BIT(30) /* Cursor Control Bit 0 */
#define IMX21LCDC_LCPR_CC1 BIT(31) /* Cursor Control Bit 1 */
/* Values HSYNC, VSYNC and Framesize Register */
#define IMX21LCDC_LHCR_HWIDTH GENMASK(31, 26)
#define IMX21LCDC_LHCR_HFPORCH GENMASK(15, 8) /* H_WAIT_1 in the i.MX25 Reference manual */
#define IMX21LCDC_LHCR_HBPORCH GENMASK(7, 0) /* H_WAIT_2 in the i.MX25 Reference manual */
#define IMX21LCDC_LVCR_VWIDTH GENMASK(31, 26)
#define IMX21LCDC_LVCR_VFPORCH GENMASK(15, 8) /* V_WAIT_1 in the i.MX25 Reference manual */
#define IMX21LCDC_LVCR_VBPORCH GENMASK(7, 0) /* V_WAIT_2 in the i.MX25 Reference manual */
#define IMX21LCDC_LSR_XMAX GENMASK(25, 20)
#define IMX21LCDC_LSR_YMAX GENMASK(9, 0)
/* Values for LPCR Register */
#define IMX21LCDC_LPCR_PCD GENMASK(5, 0)
#define IMX21LCDC_LPCR_SHARP BIT(6)
#define IMX21LCDC_LPCR_SCLKSEL BIT(7)
#define IMX21LCDC_LPCR_ACD GENMASK(14, 8)
#define IMX21LCDC_LPCR_ACDSEL BIT(15)
#define IMX21LCDC_LPCR_REV_VS BIT(16)
#define IMX21LCDC_LPCR_SWAP_SEL BIT(17)
#define IMX21LCDC_LPCR_END_SEL BIT(18)
#define IMX21LCDC_LPCR_SCLKIDLE BIT(19)
#define IMX21LCDC_LPCR_OEPOL BIT(20)
#define IMX21LCDC_LPCR_CLKPOL BIT(21)
#define IMX21LCDC_LPCR_LPPOL BIT(22)
#define IMX21LCDC_LPCR_FLMPOL BIT(23)
#define IMX21LCDC_LPCR_PIXPOL BIT(24)
#define IMX21LCDC_LPCR_BPIX GENMASK(27, 25)
#define IMX21LCDC_LPCR_PBSIZ GENMASK(29, 28)
#define IMX21LCDC_LPCR_COLOR BIT(30)
#define IMX21LCDC_LPCR_TFT BIT(31)
#define INTR_EOF BIT(1) /* VBLANK Interrupt Bit */
#define BPP_RGB565 0x05
#define BPP_XRGB8888 0x07
#define LCDC_MIN_XRES 64
#define LCDC_MIN_YRES 64
#define LCDC_MAX_XRES 1024
#define LCDC_MAX_YRES 1024
struct imx_lcdc {
struct drm_device drm;
struct drm_simple_display_pipe pipe;
struct drm_connector *connector;
void __iomem *base;
struct clk *clk_ipg;
struct clk *clk_ahb;
struct clk *clk_per;
};
static const u32 imx_lcdc_formats[] = {
DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888,
};
static inline struct imx_lcdc *imx_lcdc_from_drmdev(struct drm_device *drm)
{
return container_of(drm, struct imx_lcdc, drm);
}
static unsigned int imx_lcdc_get_format(unsigned int drm_format)
{
switch (drm_format) {
default:
DRM_WARN("Format not supported - fallback to XRGB8888\n");
fallthrough;
case DRM_FORMAT_XRGB8888:
return BPP_XRGB8888;
case DRM_FORMAT_RGB565:
return BPP_RGB565;
}
}
static void imx_lcdc_update_hw_registers(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state,
bool mode_set)
{
struct drm_crtc *crtc = &pipe->crtc;
struct drm_plane_state *new_state = pipe->plane.state;
struct drm_framebuffer *fb = new_state->fb;
struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev);
u32 lpcr, lvcr, lhcr;
u32 framesize;
dma_addr_t addr;
addr = drm_fb_dma_get_gem_addr(fb, new_state, 0);
/* The LSSAR register specifies the LCD screen start address (SSA). */
writel(addr, lcdc->base + IMX21LCDC_LSSAR);
if (!mode_set)
return;
/* Disable PER clock to make register write possible */
if (old_state && old_state->crtc && old_state->crtc->enabled)
clk_disable_unprepare(lcdc->clk_per);
/* Framesize */
framesize = FIELD_PREP(IMX21LCDC_LSR_XMAX, crtc->mode.hdisplay >> 4) |
FIELD_PREP(IMX21LCDC_LSR_YMAX, crtc->mode.vdisplay);
writel(framesize, lcdc->base + IMX21LCDC_LSR);
/* HSYNC */
lhcr = FIELD_PREP(IMX21LCDC_LHCR_HFPORCH, crtc->mode.hsync_start - crtc->mode.hdisplay - 1) |
FIELD_PREP(IMX21LCDC_LHCR_HWIDTH, crtc->mode.hsync_end - crtc->mode.hsync_start - 1) |
FIELD_PREP(IMX21LCDC_LHCR_HBPORCH, crtc->mode.htotal - crtc->mode.hsync_end - 3);
writel(lhcr, lcdc->base + IMX21LCDC_LHCR);
/* VSYNC */
lvcr = FIELD_PREP(IMX21LCDC_LVCR_VFPORCH, crtc->mode.vsync_start - crtc->mode.vdisplay) |
FIELD_PREP(IMX21LCDC_LVCR_VWIDTH, crtc->mode.vsync_end - crtc->mode.vsync_start) |
FIELD_PREP(IMX21LCDC_LVCR_VBPORCH, crtc->mode.vtotal - crtc->mode.vsync_end);
writel(lvcr, lcdc->base + IMX21LCDC_LVCR);
lpcr = readl(lcdc->base + IMX21LCDC_LPCR);
lpcr &= ~IMX21LCDC_LPCR_BPIX;
lpcr |= FIELD_PREP(IMX21LCDC_LPCR_BPIX, imx_lcdc_get_format(fb->format->format));
writel(lpcr, lcdc->base + IMX21LCDC_LPCR);
/* Virtual Page Width */
writel(new_state->fb->pitches[0] / 4, lcdc->base + IMX21LCDC_LVPWR);
/* Enable PER clock */
if (new_state->crtc->enabled)
clk_prepare_enable(lcdc->clk_per);
}
static void imx_lcdc_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state,
struct drm_plane_state *plane_state)
{
int ret;
int clk_div;
int bpp;
struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev);
struct drm_display_mode *mode = &pipe->crtc.mode;
struct drm_display_info *disp_info = &lcdc->connector->display_info;
const int hsync_pol = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : 1;
const int vsync_pol = (mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : 1;
const int data_enable_pol =
(disp_info->bus_flags & DRM_BUS_FLAG_DE_HIGH) ? 0 : 1;
const int clk_pol =
(disp_info->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE) ? 0 : 1;
clk_div = DIV_ROUND_CLOSEST_ULL(clk_get_rate(lcdc->clk_per),
mode->clock * 1000);
bpp = imx_lcdc_get_format(plane_state->fb->format->format);
writel(FIELD_PREP(IMX21LCDC_LPCR_PCD, clk_div - 1) |
FIELD_PREP(IMX21LCDC_LPCR_LPPOL, hsync_pol) |
FIELD_PREP(IMX21LCDC_LPCR_FLMPOL, vsync_pol) |
FIELD_PREP(IMX21LCDC_LPCR_OEPOL, data_enable_pol) |
FIELD_PREP(IMX21LCDC_LPCR_TFT, 1) |
FIELD_PREP(IMX21LCDC_LPCR_COLOR, 1) |
FIELD_PREP(IMX21LCDC_LPCR_PBSIZ, 3) |
FIELD_PREP(IMX21LCDC_LPCR_BPIX, bpp) |
FIELD_PREP(IMX21LCDC_LPCR_SCLKSEL, 1) |
FIELD_PREP(IMX21LCDC_LPCR_PIXPOL, 0) |
FIELD_PREP(IMX21LCDC_LPCR_CLKPOL, clk_pol),
lcdc->base + IMX21LCDC_LPCR);
/* 0px panning offset */
writel(0x00000000, lcdc->base + IMX21LCDC_LPOR);
/* disable hardware cursor */
writel(readl(lcdc->base + IMX21LCDC_LCPR) & ~(IMX21LCDC_LCPR_CC0 | IMX21LCDC_LCPR_CC1),
lcdc->base + IMX21LCDC_LCPR);
ret = clk_prepare_enable(lcdc->clk_ipg);
if (ret) {
dev_err(pipe->crtc.dev->dev, "Cannot enable ipg clock: %pe\n", ERR_PTR(ret));
return;
}
ret = clk_prepare_enable(lcdc->clk_ahb);
if (ret) {
dev_err(pipe->crtc.dev->dev, "Cannot enable ahb clock: %pe\n", ERR_PTR(ret));
clk_disable_unprepare(lcdc->clk_ipg);
return;
}
imx_lcdc_update_hw_registers(pipe, NULL, true);
/* Enable VBLANK Interrupt */
writel(INTR_EOF, lcdc->base + IMX21LCDC_LIER);
}
static void imx_lcdc_pipe_disable(struct drm_simple_display_pipe *pipe)
{
struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev);
struct drm_crtc *crtc = &lcdc->pipe.crtc;
struct drm_pending_vblank_event *event;
clk_disable_unprepare(lcdc->clk_ahb);
clk_disable_unprepare(lcdc->clk_ipg);
if (pipe->crtc.enabled)
clk_disable_unprepare(lcdc->clk_per);
spin_lock_irq(&lcdc->drm.event_lock);
event = crtc->state->event;
if (event) {
crtc->state->event = NULL;
drm_crtc_send_vblank_event(crtc, event);
}
spin_unlock_irq(&lcdc->drm.event_lock);
/* Disable VBLANK Interrupt */
writel(0, lcdc->base + IMX21LCDC_LIER);
}
static int imx_lcdc_pipe_check(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *plane_state,
struct drm_crtc_state *crtc_state)
{
const struct drm_display_mode *mode = &crtc_state->mode;
const struct drm_display_mode *old_mode = &pipe->crtc.state->mode;
if (mode->hdisplay < LCDC_MIN_XRES || mode->hdisplay > LCDC_MAX_XRES ||
mode->vdisplay < LCDC_MIN_YRES || mode->vdisplay > LCDC_MAX_YRES ||
mode->hdisplay % 0x10) { /* must be multiple of 16 */
drm_err(pipe->crtc.dev, "unsupported display mode (%u x %u)\n",
mode->hdisplay, mode->vdisplay);
return -EINVAL;
}
crtc_state->mode_changed =
old_mode->hdisplay != mode->hdisplay ||
old_mode->vdisplay != mode->vdisplay;
return 0;
}
static void imx_lcdc_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state)
{
struct drm_crtc *crtc = &pipe->crtc;
struct drm_pending_vblank_event *event = crtc->state->event;
struct drm_plane_state *new_state = pipe->plane.state;
struct drm_framebuffer *fb = new_state->fb;
struct drm_framebuffer *old_fb = old_state->fb;
struct drm_crtc *old_crtc = old_state->crtc;
bool mode_changed = false;
if (old_fb && old_fb->format != fb->format)
mode_changed = true;
else if (old_crtc != crtc)
mode_changed = true;
imx_lcdc_update_hw_registers(pipe, old_state, mode_changed);
if (event) {
crtc->state->event = NULL;
spin_lock_irq(&crtc->dev->event_lock);
if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0)
drm_crtc_arm_vblank_event(crtc, event);
else
drm_crtc_send_vblank_event(crtc, event);
spin_unlock_irq(&crtc->dev->event_lock);
}
}
static const struct drm_simple_display_pipe_funcs imx_lcdc_pipe_funcs = {
.enable = imx_lcdc_pipe_enable,
.disable = imx_lcdc_pipe_disable,
.check = imx_lcdc_pipe_check,
.update = imx_lcdc_pipe_update,
};
static const struct drm_mode_config_funcs imx_lcdc_mode_config_funcs = {
.fb_create = drm_gem_fb_create_with_dirty,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
static const struct drm_mode_config_helper_funcs imx_lcdc_mode_config_helpers = {
.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
};
static void imx_lcdc_release(struct drm_device *drm)
{
struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(drm);
drm_kms_helper_poll_fini(drm);
kfree(lcdc);
}
DEFINE_DRM_GEM_DMA_FOPS(imx_lcdc_drm_fops);
static struct drm_driver imx_lcdc_drm_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
.fops = &imx_lcdc_drm_fops,
DRM_GEM_DMA_DRIVER_OPS_VMAP,
.release = imx_lcdc_release,
.name = "imx-lcdc",
.desc = "i.MX LCDC driver",
.date = "20200716",
};
static const struct of_device_id imx_lcdc_of_dev_id[] = {
{
.compatible = "fsl,imx21-lcdc",
},
{
.compatible = "fsl,imx25-lcdc",
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_lcdc_of_dev_id);
static irqreturn_t imx_lcdc_irq_handler(int irq, void *arg)
{
struct imx_lcdc *lcdc = arg;
struct drm_crtc *crtc = &lcdc->pipe.crtc;
unsigned int status;
status = readl(lcdc->base + IMX21LCDC_LISR);
if (status & INTR_EOF) {
drm_crtc_handle_vblank(crtc);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static int imx_lcdc_probe(struct platform_device *pdev)
{
struct imx_lcdc *lcdc;
struct drm_device *drm;
struct drm_bridge *bridge;
int irq;
int ret;
struct device *dev = &pdev->dev;
lcdc = devm_drm_dev_alloc(dev, &imx_lcdc_drm_driver,
struct imx_lcdc, drm);
if (!lcdc)
return -ENOMEM;
drm = &lcdc->drm;
lcdc->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(lcdc->base))
return dev_err_probe(dev, PTR_ERR(lcdc->base), "Cannot get IO memory\n");
bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0);
if (IS_ERR(bridge))
return dev_err_probe(dev, PTR_ERR(bridge), "Failed to find bridge\n");
/* Get Clocks */
lcdc->clk_ipg = devm_clk_get(dev, "ipg");
if (IS_ERR(lcdc->clk_ipg))
return dev_err_probe(dev, PTR_ERR(lcdc->clk_ipg), "Failed to get %s clk\n", "ipg");
lcdc->clk_ahb = devm_clk_get(dev, "ahb");
if (IS_ERR(lcdc->clk_ahb))
return dev_err_probe(dev, PTR_ERR(lcdc->clk_ahb), "Failed to get %s clk\n", "ahb");
lcdc->clk_per = devm_clk_get(dev, "per");
if (IS_ERR(lcdc->clk_per))
return dev_err_probe(dev, PTR_ERR(lcdc->clk_per), "Failed to get %s clk\n", "per");
ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32));
if (ret)
return dev_err_probe(dev, ret, "Cannot set DMA Mask\n");
/* Modeset init */
ret = drmm_mode_config_init(drm);
if (ret)
return dev_err_probe(dev, ret, "Cannot initialize mode configuration structure\n");
/* CRTC, Plane, Encoder */
ret = drm_simple_display_pipe_init(drm, &lcdc->pipe,
&imx_lcdc_pipe_funcs,
imx_lcdc_formats,
ARRAY_SIZE(imx_lcdc_formats), NULL, NULL);
if (ret < 0)
return dev_err_probe(drm->dev, ret, "Cannot setup simple display pipe\n");
ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
if (ret < 0)
return dev_err_probe(drm->dev, ret, "Failed to initialize vblank\n");
ret = drm_bridge_attach(&lcdc->pipe.encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret)
return dev_err_probe(drm->dev, ret, "Cannot attach bridge\n");
lcdc->connector = drm_bridge_connector_init(drm, &lcdc->pipe.encoder);
if (IS_ERR(lcdc->connector))
return dev_err_probe(drm->dev, PTR_ERR(lcdc->connector), "Cannot init bridge connector\n");
drm_connector_attach_encoder(lcdc->connector, &lcdc->pipe.encoder);
/*
* The LCDC controller does not have an enable bit. The
* controller starts directly when the clocks are enabled.
* If the clocks are enabled when the controller is not yet
* programmed with proper register values (enabled at the
* bootloader, for example) then it just goes into some undefined
* state.
* To avoid this issue, let's enable and disable LCDC IPG,
* PER and AHB clock so that we force some kind of 'reset'
* to the LCDC block.
*/
ret = clk_prepare_enable(lcdc->clk_ipg);
if (ret)
return dev_err_probe(dev, ret, "Cannot enable ipg clock\n");
clk_disable_unprepare(lcdc->clk_ipg);
ret = clk_prepare_enable(lcdc->clk_per);
if (ret)
return dev_err_probe(dev, ret, "Cannot enable per clock\n");
clk_disable_unprepare(lcdc->clk_per);
ret = clk_prepare_enable(lcdc->clk_ahb);
if (ret)
return dev_err_probe(dev, ret, "Cannot enable ahb clock\n");
clk_disable_unprepare(lcdc->clk_ahb);
drm->mode_config.min_width = LCDC_MIN_XRES;
drm->mode_config.max_width = LCDC_MAX_XRES;
drm->mode_config.min_height = LCDC_MIN_YRES;
drm->mode_config.max_height = LCDC_MAX_YRES;
drm->mode_config.preferred_depth = 16;
drm->mode_config.funcs = &imx_lcdc_mode_config_funcs;
drm->mode_config.helper_private = &imx_lcdc_mode_config_helpers;
drm_mode_config_reset(drm);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = irq;
return ret;
}
ret = devm_request_irq(dev, irq, imx_lcdc_irq_handler, 0, "imx-lcdc", lcdc);
if (ret < 0)
return dev_err_probe(drm->dev, ret, "Failed to install IRQ handler\n");
platform_set_drvdata(pdev, drm);
ret = drm_dev_register(&lcdc->drm, 0);
if (ret)
return dev_err_probe(dev, ret, "Cannot register device\n");
drm_fbdev_generic_setup(drm, 0);
return 0;
}
static int imx_lcdc_remove(struct platform_device *pdev)
{
struct drm_device *drm = platform_get_drvdata(pdev);
drm_dev_unregister(drm);
drm_atomic_helper_shutdown(drm);
return 0;
}
static void imx_lcdc_shutdown(struct platform_device *pdev)
{
drm_atomic_helper_shutdown(platform_get_drvdata(pdev));
}
static struct platform_driver imx_lcdc_driver = {
.driver = {
.name = "imx-lcdc",
.of_match_table = imx_lcdc_of_dev_id,
},
.probe = imx_lcdc_probe,
.remove = imx_lcdc_remove,
.shutdown = imx_lcdc_shutdown,
};
module_platform_driver(imx_lcdc_driver);
MODULE_AUTHOR("Marian Cichy <M.Cichy@pengutronix.de>");
MODULE_DESCRIPTION("Freescale i.MX LCDC driver");
MODULE_LICENSE("GPL");

View File

@ -15,7 +15,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_module.h>
@ -562,7 +562,7 @@ static int kmb_probe(struct platform_device *pdev)
if (ret)
goto err_register;
drm_fbdev_generic_setup(&kmb->drm, 0);
drm_fbdev_dma_setup(&kmb->drm, 0);
return 0;

View File

@ -17,7 +17,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_print.h>
@ -449,7 +449,7 @@ static int logicvc_drm_probe(struct platform_device *pdev)
preferred_bpp = 32;
break;
}
drm_fbdev_generic_setup(drm_dev, preferred_bpp);
drm_fbdev_dma_setup(drm_dev, preferred_bpp);
return 0;

View File

@ -69,7 +69,7 @@
#include <drm/drm_bridge.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
@ -94,7 +94,7 @@
#define MCDE_PID_MAJOR_VERSION_MASK 0xFF000000
static const struct drm_mode_config_funcs mcde_mode_config_funcs = {
.fb_create = drm_gem_fb_create_with_dirty,
.fb_create = drm_gem_fb_create,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
@ -237,7 +237,7 @@ static int mcde_drm_bind(struct device *dev)
if (ret < 0)
goto unbind;
drm_fbdev_generic_setup(drm, 32);
drm_fbdev_dma_setup(drm, 32);
return 0;

View File

@ -18,7 +18,7 @@
#include <drm/drm_aperture.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_modeset_helper_vtables.h>
@ -353,7 +353,7 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
if (ret)
goto uninstall_irq;
drm_fbdev_generic_setup(drm, 32);
drm_fbdev_dma_setup(drm, 32);
return 0;

View File

@ -16,7 +16,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_mode_config.h>
@ -220,7 +220,7 @@ static int lcdif_probe(struct platform_device *pdev)
if (ret)
goto err_unload;
drm_fbdev_generic_setup(drm, 32);
drm_fbdev_dma_setup(drm, 32);
return 0;

View File

@ -20,7 +20,7 @@
#include <drm/drm_bridge.h>
#include <drm/drm_connector.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
@ -365,7 +365,7 @@ static int mxsfb_probe(struct platform_device *pdev)
if (ret)
goto err_unload;
drm_fbdev_generic_setup(drm, 32);
drm_fbdev_dma_setup(drm, 32);
return 0;

View File

@ -447,7 +447,7 @@ gf100_fifo_nonstall_allow(struct nvkm_event *event, int type, int index)
spin_unlock_irqrestore(&fifo->lock, flags);
}
void
static void
gf100_fifo_nonstall_block(struct nvkm_event *event, int type, int index)
{
struct nvkm_fifo *fifo = container_of(event, typeof(*fifo), nonstall.event);

View File

@ -30,7 +30,7 @@
#include <subdev/timer.h>
#include <subdev/top.h>
struct nvkm_cgrp *
static struct nvkm_cgrp *
nvkm_engn_cgrp_get(struct nvkm_engn *engn, unsigned long *pirqflags)
{
struct nvkm_cgrp *cgrp = NULL;

View File

@ -45,7 +45,7 @@ wpr_header_v1_dump(struct nvkm_subdev *subdev, const struct wpr_header_v1 *hdr)
nvkm_debug(subdev, "\tstatus : %d\n", hdr->status);
}
void
static void
wpr_generic_header_dump(struct nvkm_subdev *subdev, const struct wpr_generic_header *hdr)
{
nvkm_debug(subdev, "wprGenericHeader\n");

View File

@ -2,7 +2,7 @@
config DRM_OMAP
tristate "OMAP DRM"
depends on DRM && OF
depends on ARCH_OMAP2PLUS || ARCH_MULTIPLATFORM
depends on ARCH_OMAP2PLUS
select DRM_KMS_HELPER
select VIDEOMODE_HELPERS
select HDMI

View File

@ -388,6 +388,16 @@ config DRM_PANEL_NOVATEK_NT35950
Sharp panels used in Sony Xperia Z5 Premium and XZ Premium
mobile phones.
config DRM_PANEL_NOVATEK_NT36523
tristate "Novatek NT36523 panel driver"
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
help
Say Y here if you want to enable support for the panels built
around the Novatek NT36523 display controller, such as some
Boe panels used in Xiaomi Mi Pad 5 and 5 Pro tablets.
config DRM_PANEL_NOVATEK_NT36672A
tristate "Novatek NT36672A DSI panel"
depends on OF
@ -696,6 +706,16 @@ config DRM_PANEL_SONY_ACX565AKM
Say Y here if you want to enable support for the Sony ACX565AKM
800x600 3.5" panel (found on the Nokia N900).
config DRM_PANEL_SONY_TD4353_JDI
tristate "Sony TD4353 JDI panel"
depends on GPIOLIB && OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
help
Say Y here if you want to enable support for the Sony Tama
TD4353 JDI command mode panel as found on some Sony Xperia
XZ2 and XZ2 Compact smartphones.
config DRM_PANEL_SONY_TULIP_TRULY_NT35521
tristate "Sony Tulip Truly NT35521 panel"
depends on GPIOLIB && OF

View File

@ -36,6 +36,7 @@ obj-$(CONFIG_DRM_PANEL_NEWVISION_NV3052C) += panel-newvision-nv3052c.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_NT36523) += panel-novatek-nt36523.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672A) += panel-novatek-nt36672a.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o
obj-$(CONFIG_DRM_PANEL_MANTIX_MLAF057WE51) += panel-mantix-mlaf057we51.o
@ -70,6 +71,7 @@ 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_ACX565AKM) += panel-sony-acx565akm.o
obj-$(CONFIG_DRM_PANEL_SONY_TD4353_JDI) += panel-sony-td4353-jdi.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
obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o

View File

@ -1131,6 +1131,103 @@ static const struct panel_init_cmd auo_b101uan08_3_init_cmd[] = {
{},
};
static const struct panel_init_cmd starry_qfh032011_53g_init_cmd[] = {
_INIT_DCS_CMD(0xB0, 0x01),
_INIT_DCS_CMD(0xC3, 0x4F),
_INIT_DCS_CMD(0xC4, 0x40),
_INIT_DCS_CMD(0xC5, 0x40),
_INIT_DCS_CMD(0xC6, 0x40),
_INIT_DCS_CMD(0xC7, 0x40),
_INIT_DCS_CMD(0xC8, 0x4D),
_INIT_DCS_CMD(0xC9, 0x52),
_INIT_DCS_CMD(0xCA, 0x51),
_INIT_DCS_CMD(0xCD, 0x5D),
_INIT_DCS_CMD(0xCE, 0x5B),
_INIT_DCS_CMD(0xCF, 0x4B),
_INIT_DCS_CMD(0xD0, 0x49),
_INIT_DCS_CMD(0xD1, 0x47),
_INIT_DCS_CMD(0xD2, 0x45),
_INIT_DCS_CMD(0xD3, 0x41),
_INIT_DCS_CMD(0xD7, 0x50),
_INIT_DCS_CMD(0xD8, 0x40),
_INIT_DCS_CMD(0xD9, 0x40),
_INIT_DCS_CMD(0xDA, 0x40),
_INIT_DCS_CMD(0xDB, 0x40),
_INIT_DCS_CMD(0xDC, 0x4E),
_INIT_DCS_CMD(0xDD, 0x52),
_INIT_DCS_CMD(0xDE, 0x51),
_INIT_DCS_CMD(0xE1, 0x5E),
_INIT_DCS_CMD(0xE2, 0x5C),
_INIT_DCS_CMD(0xE3, 0x4C),
_INIT_DCS_CMD(0xE4, 0x4A),
_INIT_DCS_CMD(0xE5, 0x48),
_INIT_DCS_CMD(0xE6, 0x46),
_INIT_DCS_CMD(0xE7, 0x42),
_INIT_DCS_CMD(0xB0, 0x03),
_INIT_DCS_CMD(0xBE, 0x03),
_INIT_DCS_CMD(0xCC, 0x44),
_INIT_DCS_CMD(0xC8, 0x07),
_INIT_DCS_CMD(0xC9, 0x05),
_INIT_DCS_CMD(0xCA, 0x42),
_INIT_DCS_CMD(0xCD, 0x3E),
_INIT_DCS_CMD(0xCF, 0x60),
_INIT_DCS_CMD(0xD2, 0x04),
_INIT_DCS_CMD(0xD3, 0x04),
_INIT_DCS_CMD(0xD4, 0x01),
_INIT_DCS_CMD(0xD5, 0x00),
_INIT_DCS_CMD(0xD6, 0x03),
_INIT_DCS_CMD(0xD7, 0x04),
_INIT_DCS_CMD(0xD9, 0x01),
_INIT_DCS_CMD(0xDB, 0x01),
_INIT_DCS_CMD(0xE4, 0xF0),
_INIT_DCS_CMD(0xE5, 0x0A),
_INIT_DCS_CMD(0xB0, 0x00),
_INIT_DCS_CMD(0xCC, 0x08),
_INIT_DCS_CMD(0xC2, 0x08),
_INIT_DCS_CMD(0xC4, 0x10),
_INIT_DCS_CMD(0xB0, 0x02),
_INIT_DCS_CMD(0xC0, 0x00),
_INIT_DCS_CMD(0xC1, 0x0A),
_INIT_DCS_CMD(0xC2, 0x20),
_INIT_DCS_CMD(0xC3, 0x24),
_INIT_DCS_CMD(0xC4, 0x23),
_INIT_DCS_CMD(0xC5, 0x29),
_INIT_DCS_CMD(0xC6, 0x23),
_INIT_DCS_CMD(0xC7, 0x1C),
_INIT_DCS_CMD(0xC8, 0x19),
_INIT_DCS_CMD(0xC9, 0x17),
_INIT_DCS_CMD(0xCA, 0x17),
_INIT_DCS_CMD(0xCB, 0x18),
_INIT_DCS_CMD(0xCC, 0x1A),
_INIT_DCS_CMD(0xCD, 0x1E),
_INIT_DCS_CMD(0xCE, 0x20),
_INIT_DCS_CMD(0xCF, 0x23),
_INIT_DCS_CMD(0xD0, 0x07),
_INIT_DCS_CMD(0xD1, 0x00),
_INIT_DCS_CMD(0xD2, 0x00),
_INIT_DCS_CMD(0xD3, 0x0A),
_INIT_DCS_CMD(0xD4, 0x13),
_INIT_DCS_CMD(0xD5, 0x1C),
_INIT_DCS_CMD(0xD6, 0x1A),
_INIT_DCS_CMD(0xD7, 0x13),
_INIT_DCS_CMD(0xD8, 0x17),
_INIT_DCS_CMD(0xD9, 0x1C),
_INIT_DCS_CMD(0xDA, 0x19),
_INIT_DCS_CMD(0xDB, 0x17),
_INIT_DCS_CMD(0xDC, 0x17),
_INIT_DCS_CMD(0xDD, 0x18),
_INIT_DCS_CMD(0xDE, 0x1A),
_INIT_DCS_CMD(0xDF, 0x1E),
_INIT_DCS_CMD(0xE0, 0x20),
_INIT_DCS_CMD(0xE1, 0x23),
_INIT_DCS_CMD(0xE2, 0x07),
_INIT_DCS_CMD(0X11),
_INIT_DELAY_CMD(120),
_INIT_DCS_CMD(0X29),
_INIT_DELAY_CMD(80),
{},
};
static inline struct boe_panel *to_boe_panel(struct drm_panel *panel)
{
return container_of(panel, struct boe_panel, base);
@ -1497,6 +1594,32 @@ static const struct panel_desc boe_tv105wum_nw0_desc = {
.init_cmds = boe_init_cmd,
};
static const struct drm_display_mode starry_qfh032011_53g_default_mode = {
.clock = 165731,
.hdisplay = 1200,
.hsync_start = 1200 + 100,
.hsync_end = 1200 + 100 + 10,
.htotal = 1200 + 100 + 10 + 100,
.vdisplay = 1920,
.vsync_start = 1920 + 14,
.vsync_end = 1920 + 14 + 10,
.vtotal = 1920 + 14 + 10 + 15,
};
static const struct panel_desc starry_qfh032011_53g_desc = {
.modes = &starry_qfh032011_53g_default_mode,
.bpc = 8,
.size = {
.width_mm = 135,
.height_mm = 216,
},
.lanes = 4,
.format = MIPI_DSI_FMT_RGB888,
.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
MIPI_DSI_MODE_LPM,
.init_cmds = starry_qfh032011_53g_init_cmd,
};
static int boe_panel_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
@ -1667,6 +1790,9 @@ static const struct of_device_id boe_of_match[] = {
{ .compatible = "innolux,hj110iz-01a",
.data = &inx_hj110iz_desc
},
{ .compatible = "starry,2081101qfh032011-53g",
.data = &starry_qfh032011_53g_desc
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, boe_of_match);

View File

@ -0,0 +1,777 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Novatek NT36523 DriverIC panels driver
*
* Copyright (c) 2022, 2023 Jianhua Lu <lujianhua000@gmail.com>
*/
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/regulator/consumer.h>
#include <drm/drm_connector.h>
#include <drm/drm_crtc.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>
#define DSI_NUM_MIN 1
#define mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, cmd, seq...) \
do { \
mipi_dsi_dcs_write_seq(dsi0, cmd, seq); \
mipi_dsi_dcs_write_seq(dsi1, cmd, seq); \
} while (0)
struct panel_info {
struct drm_panel panel;
struct mipi_dsi_device *dsi[2];
const struct panel_desc *desc;
struct gpio_desc *reset_gpio;
struct backlight_device *backlight;
struct regulator *vddio;
bool prepared;
};
struct panel_desc {
unsigned int width_mm;
unsigned int height_mm;
unsigned int bpc;
unsigned int lanes;
unsigned long mode_flags;
enum mipi_dsi_pixel_format format;
const struct drm_display_mode *modes;
unsigned int num_modes;
const struct mipi_dsi_device_info dsi_info;
int (*init_sequence)(struct panel_info *pinfo);
bool is_dual_dsi;
};
static inline struct panel_info *to_panel_info(struct drm_panel *panel)
{
return container_of(panel, struct panel_info, panel);
}
static int elish_boe_init_sequence(struct panel_info *pinfo)
{
struct mipi_dsi_device *dsi0 = pinfo->dsi[0];
struct mipi_dsi_device *dsi1 = pinfo->dsi[1];
/* No datasheet, so write magic init sequence directly */
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb9, 0x05);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x20);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x18, 0x40);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb9, 0x02);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x23);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x00, 0x80);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x01, 0x84);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x05, 0x2d);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x06, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x07, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x08, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x09, 0x45);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x11, 0x02);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x12, 0x80);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x15, 0x83);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x16, 0x0c);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x29, 0x0a);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x30, 0xff);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x31, 0xfe);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x32, 0xfd);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x33, 0xfb);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x34, 0xf8);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x35, 0xf5);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x36, 0xf3);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x37, 0xf2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x38, 0xf2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x39, 0xf2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3a, 0xef);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3b, 0xec);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3d, 0xe9);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3f, 0xe5);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x40, 0xe5);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x41, 0xe5);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x2a, 0x13);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x45, 0xff);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x46, 0xf4);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x47, 0xe7);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x48, 0xda);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x49, 0xcd);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4a, 0xc0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4b, 0xb3);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4c, 0xb2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4d, 0xb2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4e, 0xb2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4f, 0x99);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x50, 0x80);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x51, 0x68);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x52, 0x66);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x53, 0x66);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x54, 0x66);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x2b, 0x0e);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x58, 0xff);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x59, 0xfb);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5a, 0xf7);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5b, 0xf3);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5c, 0xef);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5d, 0xe3);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5e, 0xda);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5f, 0xd8);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x60, 0xd8);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x61, 0xd8);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x62, 0xcb);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x63, 0xbf);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x64, 0xb3);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x65, 0xb2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x66, 0xb2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x67, 0xb2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x2a);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x25, 0x47);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x30, 0x47);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x39, 0x47);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x26);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x19, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1a, 0xe0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1b, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1c, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x2a, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x2b, 0xe0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0xf0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x84, 0x08);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x85, 0x0c);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x20);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x51, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x25);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x91, 0x1f);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x92, 0x0f);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x93, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x94, 0x18);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x95, 0x03);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x96, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb0, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x25);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x19, 0x1f);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1b, 0x1b);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x24);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb8, 0x28);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x27);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd0, 0x31);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd1, 0x20);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd2, 0x30);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd4, 0x08);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xde, 0x80);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xdf, 0x02);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x26);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x00, 0x81);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x01, 0xb0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x22);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x9f, 0x50);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x6f, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x70, 0x11);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x73, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x74, 0x49);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x76, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x77, 0x49);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xa0, 0x3f);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xa9, 0x50);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xaa, 0x28);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xab, 0x28);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xad, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb8, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb9, 0x49);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xba, 0x49);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbb, 0x49);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbe, 0x04);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbf, 0x49);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc0, 0x04);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc1, 0x59);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc2, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc5, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc6, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc7, 0x48);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xca, 0x43);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xcb, 0x3c);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xce, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xcf, 0x43);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd0, 0x3c);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd3, 0x43);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd4, 0x3c);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd7, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xdc, 0x43);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xdd, 0x3c);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xe1, 0x43);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xe2, 0x3c);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xf2, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xf3, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xf4, 0x48);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x25);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x13, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x14, 0x23);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbc, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbd, 0x23);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x2a);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x97, 0x3c);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x98, 0x02);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x99, 0x95);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x9a, 0x03);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x9b, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x9c, 0x0b);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x9d, 0x0a);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x9e, 0x90);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x22);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x9f, 0x50);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x23);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xa3, 0x50);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0xe0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x14, 0x60);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x16, 0xc0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4f, 0x02);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0xf0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3a, 0x08);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0xd0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x02, 0xaf);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x09, 0xee);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1c, 0x99);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1d, 0x09);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x51, 0x0f, 0xff);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x53, 0x2c);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x35, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbb, 0x13);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3b, 0x03, 0xac, 0x1a, 0x04, 0x04);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x11);
msleep(70);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x29);
return 0;
}
static int elish_csot_init_sequence(struct panel_info *pinfo)
{
struct mipi_dsi_device *dsi0 = pinfo->dsi[0];
struct mipi_dsi_device *dsi1 = pinfo->dsi[1];
/* No datasheet, so write magic init sequence directly */
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb9, 0x05);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x20);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x18, 0x40);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb9, 0x02);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0xd0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x02, 0xaf);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x00, 0x30);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x09, 0xee);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1c, 0x99);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1d, 0x09);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0xf0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3a, 0x08);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0xe0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4f, 0x02);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x20);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x58, 0x40);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x35, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x23);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x00, 0x80);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x01, 0x84);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x05, 0x2d);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x06, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x07, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x08, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x09, 0x45);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x11, 0x02);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x12, 0x80);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x15, 0x83);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x16, 0x0c);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x29, 0x0a);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x30, 0xff);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x31, 0xfe);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x32, 0xfd);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x33, 0xfb);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x34, 0xf8);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x35, 0xf5);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x36, 0xf3);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x37, 0xf2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x38, 0xf2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x39, 0xf2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3a, 0xef);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3b, 0xec);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3d, 0xe9);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3f, 0xe5);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x40, 0xe5);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x41, 0xe5);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x2a, 0x13);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x45, 0xff);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x46, 0xf4);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x47, 0xe7);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x48, 0xda);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x49, 0xcd);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4a, 0xc0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4b, 0xb3);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4c, 0xb2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4d, 0xb2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4e, 0xb2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4f, 0x99);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x50, 0x80);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x51, 0x68);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x52, 0x66);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x53, 0x66);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x54, 0x66);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x2b, 0x0e);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x58, 0xff);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x59, 0xfb);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5a, 0xf7);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5b, 0xf3);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5c, 0xef);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5d, 0xe3);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5e, 0xda);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5f, 0xd8);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x60, 0xd8);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x61, 0xd8);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x62, 0xcb);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x63, 0xbf);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x64, 0xb3);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x65, 0xb2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x66, 0xb2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x67, 0xb2);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x51, 0x0f, 0xff);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x53, 0x2c);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x55, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbb, 0x13);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3b, 0x03, 0xac, 0x1a, 0x04, 0x04);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x2a);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x25, 0x46);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x30, 0x46);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x39, 0x46);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x26);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x01, 0xb0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x19, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1a, 0xe0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1b, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1c, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x2a, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x2b, 0xe0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0xf0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x84, 0x08);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x85, 0x0c);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x20);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x51, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x25);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x91, 0x1f);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x92, 0x0f);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x93, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x94, 0x18);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x95, 0x03);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x96, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb0, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x25);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x19, 0x1f);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1b, 0x1b);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x24);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb8, 0x28);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x27);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd0, 0x31);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd1, 0x20);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd4, 0x08);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xde, 0x80);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xdf, 0x02);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x26);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x00, 0x81);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x01, 0xb0);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x22);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x6f, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x70, 0x11);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x73, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x74, 0x4d);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xa0, 0x3f);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xa9, 0x50);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xaa, 0x28);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xab, 0x28);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xad, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb8, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb9, 0x4b);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xba, 0x96);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbb, 0x4b);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbe, 0x07);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbf, 0x4b);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc0, 0x07);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc1, 0x5c);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc2, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc5, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc6, 0x3f);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc7, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xca, 0x08);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xcb, 0x40);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xce, 0x00);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xcf, 0x08);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd0, 0x40);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd3, 0x08);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd4, 0x40);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x25);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbc, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbd, 0x1c);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x2a);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x9a, 0x03);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x11);
msleep(70);
mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x29);
return 0;
}
static const struct drm_display_mode elish_boe_modes[] = {
{
/* There is only one 120 Hz timing, but it doesn't work perfectly, 104 Hz preferred */
.clock = (1600 + 60 + 8 + 60) * (2560 + 26 + 4 + 168) * 104 / 1000,
.hdisplay = 1600,
.hsync_start = 1600 + 60,
.hsync_end = 1600 + 60 + 8,
.htotal = 1600 + 60 + 8 + 60,
.vdisplay = 2560,
.vsync_start = 2560 + 26,
.vsync_end = 2560 + 26 + 4,
.vtotal = 2560 + 26 + 4 + 168,
},
};
static const struct drm_display_mode elish_csot_modes[] = {
{
/* There is only one 120 Hz timing, but it doesn't work perfectly, 104 Hz preferred */
.clock = (1600 + 200 + 40 + 52) * (2560 + 26 + 4 + 168) * 104 / 1000,
.hdisplay = 1600,
.hsync_start = 1600 + 200,
.hsync_end = 1600 + 200 + 40,
.htotal = 1600 + 200 + 40 + 52,
.vdisplay = 2560,
.vsync_start = 2560 + 26,
.vsync_end = 2560 + 26 + 4,
.vtotal = 2560 + 26 + 4 + 168,
},
};
static const struct panel_desc elish_boe_desc = {
.modes = elish_boe_modes,
.num_modes = ARRAY_SIZE(elish_boe_modes),
.dsi_info = {
.type = "BOE-elish",
.channel = 0,
.node = NULL,
},
.width_mm = 127,
.height_mm = 203,
.bpc = 8,
.lanes = 3,
.format = MIPI_DSI_FMT_RGB888,
.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
.init_sequence = elish_boe_init_sequence,
.is_dual_dsi = true,
};
static const struct panel_desc elish_csot_desc = {
.modes = elish_csot_modes,
.num_modes = ARRAY_SIZE(elish_csot_modes),
.dsi_info = {
.type = "CSOT-elish",
.channel = 0,
.node = NULL,
},
.width_mm = 127,
.height_mm = 203,
.bpc = 8,
.lanes = 3,
.format = MIPI_DSI_FMT_RGB888,
.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
.init_sequence = elish_csot_init_sequence,
.is_dual_dsi = true,
};
static void nt36523_reset(struct panel_info *pinfo)
{
gpiod_set_value_cansleep(pinfo->reset_gpio, 1);
usleep_range(12000, 13000);
gpiod_set_value_cansleep(pinfo->reset_gpio, 0);
usleep_range(12000, 13000);
gpiod_set_value_cansleep(pinfo->reset_gpio, 1);
usleep_range(12000, 13000);
gpiod_set_value_cansleep(pinfo->reset_gpio, 0);
usleep_range(12000, 13000);
}
static int nt36523_prepare(struct drm_panel *panel)
{
struct panel_info *pinfo = to_panel_info(panel);
int ret;
if (pinfo->prepared)
return 0;
ret = regulator_enable(pinfo->vddio);
if (ret) {
dev_err(panel->dev, "failed to enable vddio regulator: %d\n", ret);
return ret;
}
nt36523_reset(pinfo);
ret = pinfo->desc->init_sequence(pinfo);
if (ret < 0) {
regulator_disable(pinfo->vddio);
dev_err(panel->dev, "failed to initialize panel: %d\n", ret);
return ret;
}
pinfo->prepared = true;
return 0;
}
static int nt36523_disable(struct drm_panel *panel)
{
struct panel_info *pinfo = to_panel_info(panel);
int i, ret;
for (i = 0; i < DSI_NUM_MIN + pinfo->desc->is_dual_dsi; i++) {
ret = mipi_dsi_dcs_set_display_off(pinfo->dsi[i]);
if (ret < 0)
dev_err(&pinfo->dsi[i]->dev, "failed to set display off: %d\n", ret);
}
for (i = 0; i < DSI_NUM_MIN + pinfo->desc->is_dual_dsi; i++) {
ret = mipi_dsi_dcs_enter_sleep_mode(pinfo->dsi[i]);
if (ret < 0)
dev_err(&pinfo->dsi[i]->dev, "failed to enter sleep mode: %d\n", ret);
}
msleep(70);
return 0;
}
static int nt36523_unprepare(struct drm_panel *panel)
{
struct panel_info *pinfo = to_panel_info(panel);
if (!pinfo->prepared)
return 0;
gpiod_set_value_cansleep(pinfo->reset_gpio, 1);
regulator_disable(pinfo->vddio);
pinfo->prepared = false;
return 0;
}
static void nt36523_remove(struct mipi_dsi_device *dsi)
{
struct panel_info *pinfo = mipi_dsi_get_drvdata(dsi);
int ret;
ret = mipi_dsi_detach(pinfo->dsi[0]);
if (ret < 0)
dev_err(&dsi->dev, "failed to detach from DSI0 host: %d\n", ret);
if (pinfo->desc->is_dual_dsi) {
ret = mipi_dsi_detach(pinfo->dsi[1]);
if (ret < 0)
dev_err(&pinfo->dsi[1]->dev, "failed to detach from DSI1 host: %d\n", ret);
mipi_dsi_device_unregister(pinfo->dsi[1]);
}
drm_panel_remove(&pinfo->panel);
}
static int nt36523_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
struct panel_info *pinfo = to_panel_info(panel);
int i;
for (i = 0; i < pinfo->desc->num_modes; i++) {
const struct drm_display_mode *m = &pinfo->desc->modes[i];
struct drm_display_mode *mode;
mode = drm_mode_duplicate(connector->dev, m);
if (!mode) {
dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
m->hdisplay, m->vdisplay, drm_mode_vrefresh(m));
return -ENOMEM;
}
mode->type = DRM_MODE_TYPE_DRIVER;
if (i == 0)
mode->type |= DRM_MODE_TYPE_PREFERRED;
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode);
}
connector->display_info.width_mm = pinfo->desc->width_mm;
connector->display_info.height_mm = pinfo->desc->height_mm;
connector->display_info.bpc = pinfo->desc->bpc;
return pinfo->desc->num_modes;
}
static const struct drm_panel_funcs nt36523_panel_funcs = {
.disable = nt36523_disable,
.prepare = nt36523_prepare,
.unprepare = nt36523_unprepare,
.get_modes = nt36523_get_modes,
};
static int nt36523_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
struct device_node *dsi1;
struct mipi_dsi_host *dsi1_host;
struct panel_info *pinfo;
const struct mipi_dsi_device_info *info;
int i, ret;
pinfo = devm_kzalloc(dev, sizeof(*pinfo), GFP_KERNEL);
if (!pinfo)
return -ENOMEM;
pinfo->vddio = devm_regulator_get(dev, "vddio");
if (IS_ERR(pinfo->vddio))
return dev_err_probe(dev, PTR_ERR(pinfo->vddio), "failed to get vddio regulator\n");
pinfo->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(pinfo->reset_gpio))
return dev_err_probe(dev, PTR_ERR(pinfo->reset_gpio), "failed to get reset gpio\n");
pinfo->desc = of_device_get_match_data(dev);
if (!pinfo->desc)
return -ENODEV;
/* If the panel is dual dsi, register DSI1 */
if (pinfo->desc->is_dual_dsi) {
info = &pinfo->desc->dsi_info;
dsi1 = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);
if (!dsi1) {
dev_err(dev, "cannot get secondary DSI node.\n");
return -ENODEV;
}
dsi1_host = of_find_mipi_dsi_host_by_node(dsi1);
of_node_put(dsi1);
if (!dsi1_host)
return dev_err_probe(dev, -EPROBE_DEFER, "cannot get secondary DSI host\n");
pinfo->dsi[1] = mipi_dsi_device_register_full(dsi1_host, info);
if (!pinfo->dsi[1]) {
dev_err(dev, "cannot get secondary DSI device\n");
return -ENODEV;
}
}
pinfo->dsi[0] = dsi;
mipi_dsi_set_drvdata(dsi, pinfo);
drm_panel_init(&pinfo->panel, dev, &nt36523_panel_funcs, DRM_MODE_CONNECTOR_DSI);
ret = drm_panel_of_backlight(&pinfo->panel);
if (ret)
return dev_err_probe(dev, ret, "failed to get backlight\n");
drm_panel_add(&pinfo->panel);
for (i = 0; i < DSI_NUM_MIN + pinfo->desc->is_dual_dsi; i++) {
pinfo->dsi[i]->lanes = pinfo->desc->lanes;
pinfo->dsi[i]->format = pinfo->desc->format;
pinfo->dsi[i]->mode_flags = pinfo->desc->mode_flags;
ret = mipi_dsi_attach(pinfo->dsi[i]);
if (ret < 0)
return dev_err_probe(dev, ret, "cannot attach to DSI%d host.\n", i);
}
return 0;
}
static const struct of_device_id nt36523_of_match[] = {
{
.compatible = "xiaomi,elish-boe-nt36523",
.data = &elish_boe_desc,
},
{
.compatible = "xiaomi,elish-csot-nt36523",
.data = &elish_csot_desc,
},
{},
};
MODULE_DEVICE_TABLE(of, nt36523_of_match);
static struct mipi_dsi_driver nt36523_driver = {
.probe = nt36523_probe,
.remove = nt36523_remove,
.driver = {
.name = "panel-novatek-nt36523",
.of_match_table = nt36523_of_match,
},
};
module_mipi_dsi_driver(nt36523_driver);
MODULE_AUTHOR("Jianhua Lu <lujianhua000@gmail.com>");
MODULE_DESCRIPTION("DRM driver for Novatek NT36523 based MIPI DSI panels");
MODULE_LICENSE("GPL");

View File

@ -7,6 +7,7 @@
*/
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/media-bus-format.h>
#include <linux/module.h>
#include <linux/of.h>
@ -48,6 +49,7 @@ struct seiko_panel {
const struct seiko_panel_desc *desc;
struct regulator *dvdd;
struct regulator *avdd;
struct gpio_desc *enable_gpio;
};
static inline struct seiko_panel *to_seiko_panel(struct drm_panel *panel)
@ -139,6 +141,8 @@ static int seiko_panel_unprepare(struct drm_panel *panel)
if (!p->prepared)
return 0;
gpiod_set_value_cansleep(p->enable_gpio, 0);
regulator_disable(p->avdd);
/* Add a 100ms delay as per the panel datasheet */
@ -174,6 +178,8 @@ static int seiko_panel_prepare(struct drm_panel *panel)
goto disable_dvdd;
}
gpiod_set_value_cansleep(p->enable_gpio, 1);
p->prepared = true;
return 0;
@ -252,6 +258,12 @@ static int seiko_panel_probe(struct device *dev,
if (IS_ERR(panel->avdd))
return PTR_ERR(panel->avdd);
panel->enable_gpio = devm_gpiod_get_optional(dev, "enable",
GPIOD_OUT_LOW);
if (IS_ERR(panel->enable_gpio))
return dev_err_probe(dev, PTR_ERR(panel->enable_gpio),
"failed to request GPIO\n");
drm_panel_init(&panel->base, dev, &seiko_panel_funcs,
DRM_MODE_CONNECTOR_DPI);

View File

@ -135,6 +135,7 @@ struct st7701 {
struct regulator_bulk_data supplies[2];
struct gpio_desc *reset;
unsigned int sleep_delay;
enum drm_panel_orientation orientation;
};
static inline struct st7701 *panel_to_st7701(struct drm_panel *panel)
@ -397,6 +398,31 @@ static void dmt028vghmcmi_1a_gip_sequence(struct st7701 *st7701)
ST7701_DSI(st7701, 0x3A, 0x70);
}
static void kd50t048a_gip_sequence(struct st7701 *st7701)
{
/**
* ST7701_SPEC_V1.2 is unable to provide enough information above this
* specific command sequence, so grab the same from vendor BSP driver.
*/
ST7701_DSI(st7701, 0xE0, 0x00, 0x00, 0x02);
ST7701_DSI(st7701, 0xE1, 0x08, 0x00, 0x0A, 0x00, 0x07, 0x00, 0x09,
0x00, 0x00, 0x33, 0x33);
ST7701_DSI(st7701, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
ST7701_DSI(st7701, 0xE3, 0x00, 0x00, 0x33, 0x33);
ST7701_DSI(st7701, 0xE4, 0x44, 0x44);
ST7701_DSI(st7701, 0xE5, 0x0E, 0x60, 0xA0, 0xA0, 0x10, 0x60, 0xA0,
0xA0, 0x0A, 0x60, 0xA0, 0xA0, 0x0C, 0x60, 0xA0, 0xA0);
ST7701_DSI(st7701, 0xE6, 0x00, 0x00, 0x33, 0x33);
ST7701_DSI(st7701, 0xE7, 0x44, 0x44);
ST7701_DSI(st7701, 0xE8, 0x0D, 0x60, 0xA0, 0xA0, 0x0F, 0x60, 0xA0,
0xA0, 0x09, 0x60, 0xA0, 0xA0, 0x0B, 0x60, 0xA0, 0xA0);
ST7701_DSI(st7701, 0xEB, 0x02, 0x01, 0xE4, 0xE4, 0x44, 0x00, 0x40);
ST7701_DSI(st7701, 0xEC, 0x02, 0x01);
ST7701_DSI(st7701, 0xED, 0xAB, 0x89, 0x76, 0x54, 0x01, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x10, 0x45, 0x67, 0x98, 0xBA);
}
static int st7701_prepare(struct drm_panel *panel)
{
struct st7701 *st7701 = panel_to_st7701(panel);
@ -489,15 +515,29 @@ static int st7701_get_modes(struct drm_panel *panel,
connector->display_info.width_mm = desc_mode->width_mm;
connector->display_info.height_mm = desc_mode->height_mm;
/*
* TODO: Remove once all drm drivers call
* drm_connector_set_orientation_from_panel()
*/
drm_connector_set_panel_orientation(connector, st7701->orientation);
return 1;
}
static enum drm_panel_orientation st7701_get_orientation(struct drm_panel *panel)
{
struct st7701 *st7701 = panel_to_st7701(panel);
return st7701->orientation;
}
static const struct drm_panel_funcs st7701_funcs = {
.disable = st7701_disable,
.unprepare = st7701_unprepare,
.prepare = st7701_prepare,
.enable = st7701_enable,
.get_modes = st7701_get_modes,
.get_orientation = st7701_get_orientation,
};
static const struct drm_display_mode ts8550b_mode = {
@ -700,6 +740,105 @@ static const struct st7701_panel_desc dmt028vghmcmi_1a_desc = {
.gip_sequence = dmt028vghmcmi_1a_gip_sequence,
};
static const struct drm_display_mode kd50t048a_mode = {
.clock = 27500,
.hdisplay = 480,
.hsync_start = 480 + 2,
.hsync_end = 480 + 2 + 10,
.htotal = 480 + 2 + 10 + 2,
.vdisplay = 854,
.vsync_start = 854 + 2,
.vsync_end = 854 + 2 + 2,
.vtotal = 854 + 2 + 2 + 17,
.width_mm = 69,
.height_mm = 139,
.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
};
static const struct st7701_panel_desc kd50t048a_desc = {
.mode = &kd50t048a_mode,
.lanes = 2,
.format = MIPI_DSI_FMT_RGB888,
.panel_sleep_delay = 0,
.pv_gamma = {
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) |
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC0_MASK, 0),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) |
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC4_MASK, 0xd),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) |
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC8_MASK, 0x14),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC16_MASK, 0xd),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) |
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC24_MASK, 0x10),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC52_MASK, 0x5),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC80_MASK, 0x2),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC108_MASK, 0x8),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC147_MASK, 0x8),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC175_MASK, 0x1e),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC203_MASK, 0x5),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) |
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC231_MASK, 0x13),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC239_MASK, 0x11),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 2) |
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC247_MASK, 0x23),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) |
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC251_MASK, 0x29),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) |
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC255_MASK, 0x18)
},
.nv_gamma = {
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) |
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC0_MASK, 0),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) |
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC4_MASK, 0xc),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) |
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC8_MASK, 0x14),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC16_MASK, 0xc),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) |
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC24_MASK, 0x10),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC52_MASK, 0x5),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC80_MASK, 0x3),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC108_MASK, 0x8),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC147_MASK, 0x7),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC175_MASK, 0x20),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC203_MASK, 0x5),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) |
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC231_MASK, 0x13),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC239_MASK, 0x11),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 2) |
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC247_MASK, 0x24),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) |
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC251_MASK, 0x29),
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) |
CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC255_MASK, 0x18)
},
.nlinv = 1,
.vop_uv = 4887500,
.vcom_uv = 937500,
.vgh_mv = 15000,
.vgl_mv = -9510,
.avdd_mv = 6600,
.avcl_mv = -4400,
.gamma_op_bias = OP_BIAS_MIDDLE,
.input_op_bias = OP_BIAS_MIN,
.output_op_bias = OP_BIAS_MIN,
.t2d_ns = 1600,
.t3d_ns = 10400,
.eot_en = true,
.gip_sequence = kd50t048a_gip_sequence,
};
static int st7701_dsi_probe(struct mipi_dsi_device *dsi)
{
const struct st7701_panel_desc *desc;
@ -730,6 +869,10 @@ static int st7701_dsi_probe(struct mipi_dsi_device *dsi)
return PTR_ERR(st7701->reset);
}
ret = of_drm_get_panel_orientation(dsi->dev.of_node, &st7701->orientation);
if (ret < 0)
return dev_err_probe(&dsi->dev, ret, "Failed to get orientation\n");
drm_panel_init(&st7701->panel, &dsi->dev, &st7701_funcs,
DRM_MODE_CONNECTOR_DSI);
@ -775,6 +918,7 @@ static void st7701_dsi_remove(struct mipi_dsi_device *dsi)
static const struct of_device_id st7701_of_match[] = {
{ .compatible = "densitron,dmt028vghmcmi-1a", .data = &dmt028vghmcmi_1a_desc },
{ .compatible = "elida,kd50t048a", .data = &kd50t048a_desc },
{ .compatible = "techstar,ts8550b", .data = &ts8550b_desc },
{ }
};

View File

@ -0,0 +1,329 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022 Konrad Dybcio <konrad.dybcio@somainline.org>
*
* Generated with linux-mdss-dsi-panel-driver-generator with a
* substantial amount of manual adjustments.
*
* SONY Downstream kernel calls this one:
* - "JDI ID3" for Akari (XZ2)
* - "JDI ID4" for Apollo (XZ2 Compact)
*/
#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>
enum {
TYPE_TAMA_60HZ,
/*
* Leaving room for expansion - SONY very often uses
* *truly reliably* overclockable panels on their flagships!
*/
};
struct sony_td4353_jdi {
struct drm_panel panel;
struct mipi_dsi_device *dsi;
struct regulator_bulk_data supplies[3];
struct gpio_desc *panel_reset_gpio;
struct gpio_desc *touch_reset_gpio;
bool prepared;
int type;
};
static inline struct sony_td4353_jdi *to_sony_td4353_jdi(struct drm_panel *panel)
{
return container_of(panel, struct sony_td4353_jdi, panel);
}
static int sony_td4353_jdi_on(struct sony_td4353_jdi *ctx)
{
struct mipi_dsi_device *dsi = ctx->dsi;
struct device *dev = &dsi->dev;
int ret;
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 1080 - 1);
if (ret < 0) {
dev_err(dev, "Failed to set column address: %d\n", ret);
return ret;
}
ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 2160 - 1);
if (ret < 0) {
dev_err(dev, "Failed to set page address: %d\n", ret);
return ret;
}
ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0);
if (ret < 0) {
dev_err(dev, "Failed to set tear scanline: %d\n", ret);
return ret;
}
ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
if (ret < 0) {
dev_err(dev, "Failed to set tear on: %d\n", ret);
return ret;
}
mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
if (ret < 0) {
dev_err(dev, "Failed to set pixel format: %d\n", ret);
return ret;
}
mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_PARTIAL_ROWS,
0x00, 0x00, 0x08, 0x6f);
ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
if (ret < 0) {
dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
return ret;
}
msleep(70);
mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_MEMORY_START);
ret = mipi_dsi_dcs_set_display_on(dsi);
if (ret < 0) {
dev_err(dev, "Failed to turn display on: %d\n", ret);
return ret;
}
return 0;
}
static int sony_td4353_jdi_off(struct sony_td4353_jdi *ctx)
{
struct mipi_dsi_device *dsi = ctx->dsi;
struct device *dev = &dsi->dev;
int ret;
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
ret = mipi_dsi_dcs_set_display_off(dsi);
if (ret < 0) {
dev_err(dev, "Failed to set display off: %d\n", ret);
return ret;
}
msleep(22);
ret = mipi_dsi_dcs_set_tear_off(dsi);
if (ret < 0) {
dev_err(dev, "Failed to set tear off: %d\n", ret);
return ret;
}
ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
if (ret < 0) {
dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
return ret;
}
msleep(80);
return 0;
}
static void sony_td4353_assert_reset_gpios(struct sony_td4353_jdi *ctx, int mode)
{
gpiod_set_value_cansleep(ctx->touch_reset_gpio, mode);
gpiod_set_value_cansleep(ctx->panel_reset_gpio, mode);
usleep_range(5000, 5100);
}
static int sony_td4353_jdi_prepare(struct drm_panel *panel)
{
struct sony_td4353_jdi *ctx = to_sony_td4353_jdi(panel);
struct device *dev = &ctx->dsi->dev;
int ret;
if (ctx->prepared)
return 0;
ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
if (ret < 0) {
dev_err(dev, "Failed to enable regulators: %d\n", ret);
return ret;
}
msleep(100);
sony_td4353_assert_reset_gpios(ctx, 1);
ret = sony_td4353_jdi_on(ctx);
if (ret < 0) {
dev_err(dev, "Failed to power on panel: %d\n", ret);
sony_td4353_assert_reset_gpios(ctx, 0);
regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
return ret;
}
ctx->prepared = true;
return 0;
}
static int sony_td4353_jdi_unprepare(struct drm_panel *panel)
{
struct sony_td4353_jdi *ctx = to_sony_td4353_jdi(panel);
struct device *dev = &ctx->dsi->dev;
int ret;
if (!ctx->prepared)
return 0;
ret = sony_td4353_jdi_off(ctx);
if (ret < 0)
dev_err(dev, "Failed to power off panel: %d\n", ret);
sony_td4353_assert_reset_gpios(ctx, 0);
regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
ctx->prepared = false;
return 0;
}
static const struct drm_display_mode sony_td4353_jdi_mode_tama_60hz = {
.clock = (1080 + 4 + 8 + 8) * (2160 + 259 + 8 + 8) * 60 / 1000,
.hdisplay = 1080,
.hsync_start = 1080 + 4,
.hsync_end = 1080 + 4 + 8,
.htotal = 1080 + 4 + 8 + 8,
.vdisplay = 2160,
.vsync_start = 2160 + 259,
.vsync_end = 2160 + 259 + 8,
.vtotal = 2160 + 259 + 8 + 8,
.width_mm = 64,
.height_mm = 128,
};
static int sony_td4353_jdi_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
struct sony_td4353_jdi *ctx = to_sony_td4353_jdi(panel);
struct drm_display_mode *mode = NULL;
if (ctx->type == TYPE_TAMA_60HZ)
mode = drm_mode_duplicate(connector->dev, &sony_td4353_jdi_mode_tama_60hz);
else
return -EINVAL;
if (!mode)
return -ENOMEM;
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;
}
static const struct drm_panel_funcs sony_td4353_jdi_panel_funcs = {
.prepare = sony_td4353_jdi_prepare,
.unprepare = sony_td4353_jdi_unprepare,
.get_modes = sony_td4353_jdi_get_modes,
};
static int sony_td4353_jdi_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
struct sony_td4353_jdi *ctx;
int ret;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->type = (uintptr_t)of_device_get_match_data(dev);
ctx->supplies[0].supply = "vddio";
ctx->supplies[1].supply = "vsp";
ctx->supplies[2].supply = "vsn";
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
ctx->supplies);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to get regulators\n");
ctx->panel_reset_gpio = devm_gpiod_get(dev, "panel-reset", GPIOD_ASIS);
if (IS_ERR(ctx->panel_reset_gpio))
return dev_err_probe(dev, PTR_ERR(ctx->panel_reset_gpio),
"Failed to get panel-reset-gpios\n");
ctx->touch_reset_gpio = devm_gpiod_get(dev, "touch-reset", GPIOD_ASIS);
if (IS_ERR(ctx->touch_reset_gpio))
return dev_err_probe(dev, PTR_ERR(ctx->touch_reset_gpio),
"Failed to get touch-reset-gpios\n");
ctx->dsi = dsi;
mipi_dsi_set_drvdata(dsi, ctx);
dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
drm_panel_init(&ctx->panel, dev, &sony_td4353_jdi_panel_funcs,
DRM_MODE_CONNECTOR_DSI);
ret = drm_panel_of_backlight(&ctx->panel);
if (ret)
return dev_err_probe(dev, ret, "Failed to get backlight\n");
drm_panel_add(&ctx->panel);
ret = mipi_dsi_attach(dsi);
if (ret < 0) {
dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
drm_panel_remove(&ctx->panel);
return ret;
}
return 0;
}
static void sony_td4353_jdi_remove(struct mipi_dsi_device *dsi)
{
struct sony_td4353_jdi *ctx = mipi_dsi_get_drvdata(dsi);
int ret;
ret = mipi_dsi_detach(dsi);
if (ret < 0)
dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
drm_panel_remove(&ctx->panel);
}
static const struct of_device_id sony_td4353_jdi_of_match[] = {
{ .compatible = "sony,td4353-jdi-tama", .data = (void *)TYPE_TAMA_60HZ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sony_td4353_jdi_of_match);
static struct mipi_dsi_driver sony_td4353_jdi_driver = {
.probe = sony_td4353_jdi_probe,
.remove = sony_td4353_jdi_remove,
.driver = {
.name = "panel-sony-td4353-jdi",
.of_match_table = sony_td4353_jdi_of_match,
},
};
module_mipi_dsi_driver(sony_td4353_jdi_driver);
MODULE_AUTHOR("Konrad Dybcio <konrad.dybcio@somainline.org>");
MODULE_DESCRIPTION("DRM panel driver for SONY Xperia XZ2/XZ2c JDI panel");
MODULE_LICENSE("GPL");

View File

@ -48,7 +48,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
@ -308,7 +308,7 @@ static int pl111_amba_probe(struct amba_device *amba_dev,
if (ret < 0)
goto dev_put;
drm_fbdev_generic_setup(drm, priv->variant->fb_bpp);
drm_fbdev_dma_setup(drm, priv->variant->fb_bpp);
return 0;

View File

@ -359,11 +359,6 @@ static inline void dsi_write(struct dw_mipi_dsi_rockchip *dsi, u32 reg, u32 val)
writel(val, dsi->base + reg);
}
static inline u32 dsi_read(struct dw_mipi_dsi_rockchip *dsi, u32 reg)
{
return readl(dsi->base + reg);
}
static void dw_mipi_dsi_phy_write(struct dw_mipi_dsi_rockchip *dsi,
u8 test_code,
u8 test_data)

View File

@ -74,6 +74,7 @@ struct rockchip_hdmi {
struct regmap *regmap;
struct rockchip_encoder encoder;
const struct rockchip_hdmi_chip_data *chip_data;
const struct dw_hdmi_plat_data *plat_data;
struct clk *ref_clk;
struct clk *grf_clk;
struct dw_hdmi *hdmi;
@ -160,6 +161,12 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
{ 0x214c, 0x0003},
{ 0x4064, 0x0003}
},
}, {
340000000, {
{ 0x0040, 0x0003 },
{ 0x3b4c, 0x0003 },
{ 0x5a64, 0x0003 },
},
}, {
~0UL, {
{ 0x00a0, 0x000a },
@ -185,6 +192,8 @@ static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
146250000, { 0x0038, 0x0038, 0x0038 },
}, {
148500000, { 0x0000, 0x0038, 0x0038 },
}, {
600000000, { 0x0000, 0x0000, 0x0000 },
}, {
~0UL, { 0x0000, 0x0000, 0x0000},
}
@ -241,23 +250,39 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
}
static enum drm_mode_status
dw_hdmi_rockchip_mode_valid(struct dw_hdmi *hdmi, void *data,
dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
struct rockchip_hdmi *hdmi = data;
const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
int pclk = mode->clock * 1000;
bool valid = false;
bool exact_match = hdmi->plat_data->phy_force_vendor;
int i;
for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
if (pclk == mpll_cfg[i].mpixelclock) {
valid = true;
break;
}
if (hdmi->ref_clk) {
int rpclk = clk_round_rate(hdmi->ref_clk, pclk);
if (abs(rpclk - pclk) > pclk / 1000)
return MODE_NOCLOCK;
}
return (valid) ? MODE_OK : MODE_BAD;
for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
/*
* For vendor specific phys force an exact match of the pixelclock
* to preserve the original behaviour of the driver.
*/
if (exact_match && pclk == mpll_cfg[i].mpixelclock)
return MODE_OK;
/*
* The Synopsys phy can work with pixelclocks up to the value given
* in the corresponding mpll_cfg entry.
*/
if (!exact_match && pclk <= mpll_cfg[i].mpixelclock)
return MODE_OK;
}
return MODE_BAD;
}
static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
@ -546,8 +571,10 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
return -ENOMEM;
hdmi->dev = &pdev->dev;
hdmi->plat_data = plat_data;
hdmi->chip_data = plat_data->phy_data;
plat_data->phy_data = hdmi;
plat_data->priv_data = hdmi;
encoder = &hdmi->encoder.encoder;
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);

View File

@ -1171,6 +1171,17 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
spin_unlock_irqrestore(&vop->irq_lock, flags);
}
static enum drm_mode_status vop_crtc_mode_valid(struct drm_crtc *crtc,
const struct drm_display_mode *mode)
{
struct vop *vop = to_vop(crtc);
if (vop->data->max_output.width && mode->hdisplay > vop->data->max_output.width)
return MODE_BAD_HVALUE;
return MODE_OK;
}
static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
@ -1582,6 +1593,7 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
}
static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {
.mode_valid = vop_crtc_mode_valid,
.mode_fixup = vop_crtc_mode_fixup,
.atomic_check = vop_crtc_atomic_check,
.atomic_begin = vop_crtc_atomic_begin,

View File

@ -42,6 +42,11 @@ enum vop_data_format {
VOP_FMT_YUV444SP,
};
struct vop_rect {
int width;
int height;
};
struct vop_reg {
uint32_t mask;
uint16_t offset;
@ -225,6 +230,7 @@ struct vop_data {
const struct vop_win_data *win;
unsigned int win_size;
unsigned int lut_size;
struct vop_rect max_output;
#define VOP_FEATURE_OUTPUT_RGB10 BIT(0)
#define VOP_FEATURE_INTERNAL_RGB BIT(1)

View File

@ -1438,6 +1438,8 @@ static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id,
die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX;
die |= RK3568_SYS_DSP_INFACE_EN_RGB |
FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id);
dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL;
dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags);
if (polflags & POLFLAG_DCLK_INV)
regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3));
else
@ -2655,7 +2657,7 @@ static int vop2_bind(struct device *dev, struct device *master, void *data)
return -ENODEV;
/* Allocate vop2 struct and its vop2_win array */
alloc_size = sizeof(*vop2) + sizeof(*vop2->win) * vop2_data->win_size;
alloc_size = struct_size(vop2, win, vop2_data->win_size);
vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
if (!vop2)
return -ENOMEM;

View File

@ -27,11 +27,6 @@ enum win_dly_mode {
VOP2_DLY_MODE_MAX,
};
struct vop_rect {
int width;
int height;
};
enum vop2_scale_up_mode {
VOP2_SCALE_UP_NRST_NBOR,
VOP2_SCALE_UP_BIL,

View File

@ -181,6 +181,7 @@ static const struct vop_data rk3036_vop = {
.output = &rk3036_output,
.win = rk3036_vop_win_data,
.win_size = ARRAY_SIZE(rk3036_vop_win_data),
.max_output = { 1920, 1080 },
};
static const struct vop_win_phy rk3126_win1_data = {
@ -213,6 +214,7 @@ static const struct vop_data rk3126_vop = {
.output = &rk3036_output,
.win = rk3126_vop_win_data,
.win_size = ARRAY_SIZE(rk3126_vop_win_data),
.max_output = { 1920, 1080 },
};
static const int px30_vop_intrs[] = {
@ -340,6 +342,7 @@ static const struct vop_data px30_vop_big = {
.output = &px30_output,
.win = px30_vop_big_win_data,
.win_size = ARRAY_SIZE(px30_vop_big_win_data),
.max_output = { 1920, 1080 },
};
static const struct vop_win_data px30_vop_lit_win_data[] = {
@ -356,6 +359,7 @@ static const struct vop_data px30_vop_lit = {
.output = &px30_output,
.win = px30_vop_lit_win_data,
.win_size = ARRAY_SIZE(px30_vop_lit_win_data),
.max_output = { 1920, 1080 },
};
static const struct vop_scl_regs rk3066_win_scl = {
@ -479,6 +483,7 @@ static const struct vop_data rk3066_vop = {
.output = &rk3066_output,
.win = rk3066_vop_win_data,
.win_size = ARRAY_SIZE(rk3066_vop_win_data),
.max_output = { 1920, 1080 },
};
static const struct vop_scl_regs rk3188_win_scl = {
@ -585,6 +590,7 @@ static const struct vop_data rk3188_vop = {
.win = rk3188_vop_win_data,
.win_size = ARRAY_SIZE(rk3188_vop_win_data),
.feature = VOP_FEATURE_INTERNAL_RGB,
.max_output = { 2048, 1536 },
};
static const struct vop_scl_extension rk3288_win_full_scl_ext = {
@ -732,6 +738,12 @@ static const struct vop_data rk3288_vop = {
.win = rk3288_vop_win_data,
.win_size = ARRAY_SIZE(rk3288_vop_win_data),
.lut_size = 1024,
/*
* This is the maximum resolution for the VOPB, the VOPL can only do
* 2560x1600, but we can't distinguish them as they have the same
* compatible.
*/
.max_output = { 3840, 2160 },
};
static const int rk3368_vop_intrs[] = {
@ -833,6 +845,7 @@ static const struct vop_data rk3368_vop = {
.misc = &rk3368_misc,
.win = rk3368_vop_win_data,
.win_size = ARRAY_SIZE(rk3368_vop_win_data),
.max_output = { 4096, 2160 },
};
static const struct vop_intr rk3366_vop_intr = {
@ -854,6 +867,7 @@ static const struct vop_data rk3366_vop = {
.misc = &rk3368_misc,
.win = rk3368_vop_win_data,
.win_size = ARRAY_SIZE(rk3368_vop_win_data),
.max_output = { 4096, 2160 },
};
static const struct vop_output rk3399_output = {
@ -984,6 +998,7 @@ static const struct vop_data rk3399_vop_big = {
.win_size = ARRAY_SIZE(rk3399_vop_win_data),
.win_yuv2yuv = rk3399_vop_big_win_yuv2yuv_data,
.lut_size = 1024,
.max_output = { 4096, 2160 },
};
static const struct vop_win_data rk3399_vop_lit_win_data[] = {
@ -1010,6 +1025,7 @@ static const struct vop_data rk3399_vop_lit = {
.win_size = ARRAY_SIZE(rk3399_vop_lit_win_data),
.win_yuv2yuv = rk3399_vop_lit_win_yuv2yuv_data,
.lut_size = 256,
.max_output = { 2560, 1600 },
};
static const struct vop_win_data rk3228_vop_win_data[] = {
@ -1029,6 +1045,7 @@ static const struct vop_data rk3228_vop = {
.misc = &rk3368_misc,
.win = rk3228_vop_win_data,
.win_size = ARRAY_SIZE(rk3228_vop_win_data),
.max_output = { 4096, 2160 },
};
static const struct vop_modeset rk3328_modeset = {
@ -1100,6 +1117,7 @@ static const struct vop_data rk3328_vop = {
.misc = &rk3328_misc,
.win = rk3328_vop_win_data,
.win_size = ARRAY_SIZE(rk3328_vop_win_data),
.max_output = { 4096, 2160 },
};
static const struct of_device_id vop_driver_dt_match[] = {

View File

@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
config DRM_STI
tristate "DRM Support for STMicroelectronics SoC stiH4xx Series"
depends on OF && DRM && (ARCH_STI || ARCH_MULTIPLATFORM)
depends on OF && DRM && ARCH_STI
select RESET_CONTROLLER
select DRM_KMS_HELPER
select DRM_GEM_DMA_HELPER

View File

@ -14,7 +14,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_debugfs.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_of.h>
@ -199,7 +199,7 @@ static int sti_bind(struct device *dev)
drm_mode_config_reset(ddev);
drm_fbdev_generic_setup(ddev, 32);
drm_fbdev_dma_setup(ddev, 32);
return 0;

View File

@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
config DRM_STM
tristate "DRM Support for STMicroelectronics SoC Series"
depends on DRM && (ARCH_STM32 || ARCH_MULTIPLATFORM)
depends on DRM && ARCH_STM32
select DRM_KMS_HELPER
select DRM_GEM_DMA_HELPER
select DRM_PANEL_BRIDGE

View File

@ -18,7 +18,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_module.h>
@ -203,7 +203,7 @@ static int stm_drm_platform_probe(struct platform_device *pdev)
if (ret)
goto err_put;
drm_fbdev_generic_setup(ddev, 16);
drm_fbdev_dma_setup(ddev, 16);
return 0;

View File

@ -17,7 +17,7 @@
#include <drm/drm_aperture.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_module.h>
#include <drm/drm_of.h>
@ -111,7 +111,7 @@ static int sun4i_drv_bind(struct device *dev)
if (ret)
goto finish_poll;
drm_fbdev_generic_setup(drm, 32);
drm_fbdev_dma_setup(drm, 32);
dev_set_drvdata(dev, drm);

View File

@ -409,12 +409,15 @@ static size_t conversion_buf_size(u32 dst_format, unsigned int dst_pitch,
const struct drm_rect *clip)
{
const struct drm_format_info *dst_fi = drm_format_info(dst_format);
unsigned int bpp;
if (!dst_fi)
return -EINVAL;
if (!dst_pitch)
dst_pitch = drm_rect_width(clip) * dst_fi->cpp[0];
if (!dst_pitch) {
bpp = drm_format_info_bpp(dst_fi, 0);
dst_pitch = DIV_ROUND_UP(drm_rect_width(clip) * bpp, 8);
}
return dst_pitch * drm_rect_height(clip);
}

View File

@ -19,14 +19,8 @@ static int fake_probe(struct platform_device *pdev)
return 0;
}
static int fake_remove(struct platform_device *pdev)
{
return 0;
}
static struct platform_driver fake_platform_driver = {
.probe = fake_probe,
.remove = fake_remove,
.driver = {
.name = KUNIT_DEVICE_NAME,
},

View File

@ -13,7 +13,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_module.h>
@ -179,7 +179,7 @@ static int tidss_probe(struct platform_device *pdev)
goto err_irq_uninstall;
}
drm_fbdev_generic_setup(ddev, 32);
drm_fbdev_dma_setup(ddev, 32);
dev_dbg(dev, "%s done\n", __func__);

View File

@ -16,7 +16,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_debugfs.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
@ -384,7 +384,7 @@ static int tilcdc_init(const struct drm_driver *ddrv, struct device *dev)
goto init_failed;
priv->is_registered = true;
drm_fbdev_generic_setup(ddev, bpp);
drm_fbdev_dma_setup(ddev, bpp);
return 0;
init_failed:

View File

@ -12,7 +12,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
@ -394,7 +394,7 @@ static int arcpgu_probe(struct platform_device *pdev)
if (ret)
goto err_unload;
drm_fbdev_generic_setup(&arcpgu->drm, 16);
drm_fbdev_dma_setup(&arcpgu->drm, 16);
return 0;

View File

@ -24,6 +24,7 @@
#include <video/vga.h>
#include <drm/drm_aperture.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_connector.h>
@ -43,7 +44,6 @@
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_module.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#define DRIVER_NAME "cirrus"
#define DRIVER_DESC "qemu cirrus vga"
@ -56,16 +56,34 @@
struct cirrus_device {
struct drm_device dev;
struct drm_simple_display_pipe pipe;
struct drm_connector conn;
unsigned int cpp;
unsigned int pitch;
/* modesetting pipeline */
struct drm_plane primary_plane;
struct drm_crtc crtc;
struct drm_encoder encoder;
struct drm_connector connector;
/* HW resources */
void __iomem *vram;
void __iomem *mmio;
};
#define to_cirrus(_dev) container_of(_dev, struct cirrus_device, dev)
struct cirrus_primary_plane_state {
struct drm_shadow_plane_state base;
/* HW scanout buffer */
const struct drm_format_info *format;
unsigned int pitch;
};
static inline struct cirrus_primary_plane_state *
to_cirrus_primary_plane_state(struct drm_plane_state *plane_state)
{
return container_of(plane_state, struct cirrus_primary_plane_state, base.base);
};
/* ------------------------------------------------------------------ */
/*
* The meat of this driver. The core passes us a mode and we have to program
@ -126,46 +144,42 @@ static void wreg_hdr(struct cirrus_device *cirrus, u8 val)
iowrite8(val, cirrus->mmio + VGA_DAC_MASK);
}
static int cirrus_convert_to(struct drm_framebuffer *fb)
static const struct drm_format_info *cirrus_convert_to(struct drm_framebuffer *fb)
{
if (fb->format->cpp[0] == 4 && fb->pitches[0] > CIRRUS_MAX_PITCH) {
if (fb->format->format == DRM_FORMAT_XRGB8888 && fb->pitches[0] > CIRRUS_MAX_PITCH) {
if (fb->width * 3 <= CIRRUS_MAX_PITCH)
/* convert from XR24 to RG24 */
return 3;
return drm_format_info(DRM_FORMAT_RGB888);
else
/* convert from XR24 to RG16 */
return 2;
return drm_format_info(DRM_FORMAT_RGB565);
}
return 0;
return NULL;
}
static int cirrus_cpp(struct drm_framebuffer *fb)
static const struct drm_format_info *cirrus_format(struct drm_framebuffer *fb)
{
int convert_cpp = cirrus_convert_to(fb);
const struct drm_format_info *format = cirrus_convert_to(fb);
if (convert_cpp)
return convert_cpp;
return fb->format->cpp[0];
if (format)
return format;
return fb->format;
}
static int cirrus_pitch(struct drm_framebuffer *fb)
{
int convert_cpp = cirrus_convert_to(fb);
const struct drm_format_info *format = cirrus_convert_to(fb);
if (convert_cpp)
return convert_cpp * fb->width;
if (format)
return drm_format_info_min_pitch(format, 0, fb->width);
return fb->pitches[0];
}
static void cirrus_set_start_address(struct cirrus_device *cirrus, u32 offset)
{
int idx;
u32 addr;
u8 tmp;
if (!drm_dev_enter(&cirrus->dev, &idx))
return;
addr = offset >> 2;
wreg_crt(cirrus, 0x0c, (u8)((addr >> 8) & 0xff));
wreg_crt(cirrus, 0x0d, (u8)(addr & 0xff));
@ -180,21 +194,14 @@ static void cirrus_set_start_address(struct cirrus_device *cirrus, u32 offset)
tmp &= 0x7f;
tmp |= (addr >> 12) & 0x80;
wreg_crt(cirrus, 0x1d, tmp);
drm_dev_exit(idx);
}
static int cirrus_mode_set(struct cirrus_device *cirrus,
struct drm_display_mode *mode,
struct drm_framebuffer *fb)
static void cirrus_mode_set(struct cirrus_device *cirrus,
struct drm_display_mode *mode)
{
int hsyncstart, hsyncend, htotal, hdispend;
int vtotal, vdispend;
int tmp, idx;
int sr07 = 0, hdr = 0;
if (!drm_dev_enter(&cirrus->dev, &idx))
return -1;
int tmp;
htotal = mode->htotal / 8;
hsyncend = mode->hsync_end / 8;
@ -258,46 +265,39 @@ static int cirrus_mode_set(struct cirrus_device *cirrus,
/* Disable Hercules/CGA compatibility */
wreg_crt(cirrus, VGA_CRTC_MODE, 0x03);
}
static void cirrus_format_set(struct cirrus_device *cirrus,
const struct drm_format_info *format)
{
u8 sr07, hdr;
sr07 = rreg_seq(cirrus, 0x07);
sr07 &= 0xe0;
hdr = 0;
cirrus->cpp = cirrus_cpp(fb);
switch (cirrus->cpp * 8) {
case 8:
switch (format->format) {
case DRM_FORMAT_C8:
sr07 |= 0x11;
hdr = 0x00;
break;
case 16:
case DRM_FORMAT_RGB565:
sr07 |= 0x17;
hdr = 0xc1;
break;
case 24:
case DRM_FORMAT_RGB888:
sr07 |= 0x15;
hdr = 0xc5;
break;
case 32:
case DRM_FORMAT_XRGB8888:
sr07 |= 0x19;
hdr = 0xc5;
break;
default:
drm_dev_exit(idx);
return -1;
return;
}
wreg_seq(cirrus, 0x7, sr07);
/* Program the pitch */
cirrus->pitch = cirrus_pitch(fb);
tmp = cirrus->pitch / 8;
wreg_crt(cirrus, VGA_CRTC_OFFSET, tmp);
/* Enable extended blanking and pitch bits, and enable full memory */
tmp = 0x22;
tmp |= (cirrus->pitch >> 7) & 0x10;
tmp |= (cirrus->pitch >> 6) & 0x40;
wreg_crt(cirrus, 0x1b, tmp);
/* Enable high-colour modes */
wreg_gfx(cirrus, VGA_GFX_MODE, 0x40);
@ -305,96 +305,249 @@ static int cirrus_mode_set(struct cirrus_device *cirrus,
wreg_gfx(cirrus, VGA_GFX_MISC, 0x01);
wreg_hdr(cirrus, hdr);
}
static void cirrus_pitch_set(struct cirrus_device *cirrus, unsigned int pitch)
{
u8 cr13, cr1b;
/* Program the pitch */
cr13 = pitch / 8;
wreg_crt(cirrus, VGA_CRTC_OFFSET, cr13);
/* Enable extended blanking and pitch bits, and enable full memory */
cr1b = 0x22;
cr1b |= (pitch >> 7) & 0x10;
cr1b |= (pitch >> 6) & 0x40;
wreg_crt(cirrus, 0x1b, cr1b);
cirrus_set_start_address(cirrus, 0);
/* Unblank (needed on S3 resume, vgabios doesn't do it then) */
outb(0x20, 0x3c0);
drm_dev_exit(idx);
return 0;
}
static int cirrus_fb_blit_rect(struct drm_framebuffer *fb,
const struct iosys_map *vmap,
struct drm_rect *rect)
{
struct cirrus_device *cirrus = to_cirrus(fb->dev);
struct iosys_map dst;
int idx;
if (!drm_dev_enter(&cirrus->dev, &idx))
return -ENODEV;
iosys_map_set_vaddr_iomem(&dst, cirrus->vram);
if (cirrus->cpp == fb->format->cpp[0]) {
iosys_map_incr(&dst, drm_fb_clip_offset(fb->pitches[0], fb->format, rect));
drm_fb_memcpy(&dst, fb->pitches, vmap, fb, rect);
} else if (fb->format->cpp[0] == 4 && cirrus->cpp == 2) {
iosys_map_incr(&dst, drm_fb_clip_offset(cirrus->pitch, fb->format, rect));
drm_fb_xrgb8888_to_rgb565(&dst, &cirrus->pitch, vmap, fb, rect, false);
} else if (fb->format->cpp[0] == 4 && cirrus->cpp == 3) {
iosys_map_incr(&dst, drm_fb_clip_offset(cirrus->pitch, fb->format, rect));
drm_fb_xrgb8888_to_rgb888(&dst, &cirrus->pitch, vmap, fb, rect);
} else {
WARN_ON_ONCE("cpp mismatch");
}
drm_dev_exit(idx);
return 0;
}
static int cirrus_fb_blit_fullscreen(struct drm_framebuffer *fb,
const struct iosys_map *map)
{
struct drm_rect fullscreen = {
.x1 = 0,
.x2 = fb->width,
.y1 = 0,
.y2 = fb->height,
};
return cirrus_fb_blit_rect(fb, map, &fullscreen);
}
static int cirrus_check_size(int width, int height,
struct drm_framebuffer *fb)
{
int pitch = width * 2;
if (fb)
pitch = cirrus_pitch(fb);
if (pitch > CIRRUS_MAX_PITCH)
return -EINVAL;
if (pitch * height > CIRRUS_VRAM_SIZE)
return -EINVAL;
return 0;
}
/* ------------------------------------------------------------------ */
/* cirrus connector */
/* cirrus display pipe */
static int cirrus_conn_get_modes(struct drm_connector *conn)
static const uint32_t cirrus_primary_plane_formats[] = {
DRM_FORMAT_RGB565,
DRM_FORMAT_RGB888,
DRM_FORMAT_XRGB8888,
};
static const uint64_t cirrus_primary_plane_format_modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID
};
static int cirrus_primary_plane_helper_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
struct cirrus_primary_plane_state *new_primary_plane_state =
to_cirrus_primary_plane_state(new_plane_state);
struct drm_framebuffer *fb = new_plane_state->fb;
struct drm_crtc *new_crtc = new_plane_state->crtc;
struct drm_crtc_state *new_crtc_state = NULL;
int ret;
unsigned int pitch;
if (new_crtc)
new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
DRM_PLANE_NO_SCALING,
DRM_PLANE_NO_SCALING,
false, false);
if (ret)
return ret;
else if (!new_plane_state->visible)
return 0;
pitch = cirrus_pitch(fb);
/* validate size constraints */
if (pitch > CIRRUS_MAX_PITCH)
return -EINVAL;
else if (pitch * fb->height > CIRRUS_VRAM_SIZE)
return -EINVAL;
new_primary_plane_state->format = cirrus_format(fb);
new_primary_plane_state->pitch = pitch;
return 0;
}
static void cirrus_primary_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct cirrus_device *cirrus = to_cirrus(plane->dev);
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
struct cirrus_primary_plane_state *primary_plane_state =
to_cirrus_primary_plane_state(plane_state);
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct drm_framebuffer *fb = plane_state->fb;
const struct drm_format_info *format = primary_plane_state->format;
unsigned int pitch = primary_plane_state->pitch;
struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
struct cirrus_primary_plane_state *old_primary_plane_state =
to_cirrus_primary_plane_state(old_plane_state);
struct iosys_map vaddr = IOSYS_MAP_INIT_VADDR_IOMEM(cirrus->vram);
struct drm_atomic_helper_damage_iter iter;
struct drm_rect damage;
int idx;
if (!fb)
return;
if (!drm_dev_enter(&cirrus->dev, &idx))
return;
if (old_primary_plane_state->format != format)
cirrus_format_set(cirrus, format);
if (old_primary_plane_state->pitch != pitch)
cirrus_pitch_set(cirrus, pitch);
drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
unsigned int offset = drm_fb_clip_offset(pitch, format, &damage);
struct iosys_map dst = IOSYS_MAP_INIT_OFFSET(&vaddr, offset);
drm_fb_blit(&dst, &pitch, format->format, shadow_plane_state->data, fb, &damage);
}
drm_dev_exit(idx);
}
static const struct drm_plane_helper_funcs cirrus_primary_plane_helper_funcs = {
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
.atomic_check = cirrus_primary_plane_helper_atomic_check,
.atomic_update = cirrus_primary_plane_helper_atomic_update,
};
static struct drm_plane_state *
cirrus_primary_plane_atomic_duplicate_state(struct drm_plane *plane)
{
struct drm_plane_state *plane_state = plane->state;
struct cirrus_primary_plane_state *primary_plane_state =
to_cirrus_primary_plane_state(plane_state);
struct cirrus_primary_plane_state *new_primary_plane_state;
struct drm_shadow_plane_state *new_shadow_plane_state;
if (!plane_state)
return NULL;
new_primary_plane_state = kzalloc(sizeof(*new_primary_plane_state), GFP_KERNEL);
if (!new_primary_plane_state)
return NULL;
new_shadow_plane_state = &new_primary_plane_state->base;
__drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state);
new_primary_plane_state->format = primary_plane_state->format;
new_primary_plane_state->pitch = primary_plane_state->pitch;
return &new_shadow_plane_state->base;
}
static void cirrus_primary_plane_atomic_destroy_state(struct drm_plane *plane,
struct drm_plane_state *plane_state)
{
struct cirrus_primary_plane_state *primary_plane_state =
to_cirrus_primary_plane_state(plane_state);
__drm_gem_destroy_shadow_plane_state(&primary_plane_state->base);
kfree(primary_plane_state);
}
static void cirrus_reset_primary_plane(struct drm_plane *plane)
{
struct cirrus_primary_plane_state *primary_plane_state;
if (plane->state) {
cirrus_primary_plane_atomic_destroy_state(plane, plane->state);
plane->state = NULL; /* must be set to NULL here */
}
primary_plane_state = kzalloc(sizeof(*primary_plane_state), GFP_KERNEL);
if (!primary_plane_state)
return;
__drm_gem_reset_shadow_plane(plane, &primary_plane_state->base);
}
static const struct drm_plane_funcs cirrus_primary_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
.reset = cirrus_reset_primary_plane,
.atomic_duplicate_state = cirrus_primary_plane_atomic_duplicate_state,
.atomic_destroy_state = cirrus_primary_plane_atomic_destroy_state,
};
static int cirrus_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
{
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
int ret;
if (!crtc_state->enable)
return 0;
ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
if (ret)
return ret;
return 0;
}
static void cirrus_crtc_helper_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct cirrus_device *cirrus = to_cirrus(crtc->dev);
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
int idx;
if (!drm_dev_enter(&cirrus->dev, &idx))
return;
cirrus_mode_set(cirrus, &crtc_state->mode);
/* Unblank (needed on S3 resume, vgabios doesn't do it then) */
outb(VGA_AR_ENABLE_DISPLAY, VGA_ATT_W);
drm_dev_exit(idx);
}
static const struct drm_crtc_helper_funcs cirrus_crtc_helper_funcs = {
.atomic_check = cirrus_crtc_helper_atomic_check,
.atomic_enable = cirrus_crtc_helper_atomic_enable,
};
static const struct drm_crtc_funcs cirrus_crtc_funcs = {
.reset = drm_atomic_helper_crtc_reset,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
};
static const struct drm_encoder_funcs cirrus_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static int cirrus_connector_helper_get_modes(struct drm_connector *connector)
{
int count;
count = drm_add_modes_noedid(conn,
conn->dev->mode_config.max_width,
conn->dev->mode_config.max_height);
drm_set_preferred_mode(conn, 1024, 768);
count = drm_add_modes_noedid(connector,
connector->dev->mode_config.max_width,
connector->dev->mode_config.max_height);
drm_set_preferred_mode(connector, 1024, 768);
return count;
}
static const struct drm_connector_helper_funcs cirrus_conn_helper_funcs = {
.get_modes = cirrus_conn_get_modes,
static const struct drm_connector_helper_funcs cirrus_connector_helper_funcs = {
.get_modes = cirrus_connector_helper_get_modes,
};
static const struct drm_connector_funcs cirrus_conn_funcs = {
static const struct drm_connector_funcs cirrus_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.reset = drm_atomic_helper_connector_reset,
@ -402,111 +555,73 @@ static const struct drm_connector_funcs cirrus_conn_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int cirrus_conn_init(struct cirrus_device *cirrus)
{
drm_connector_helper_add(&cirrus->conn, &cirrus_conn_helper_funcs);
return drm_connector_init(&cirrus->dev, &cirrus->conn,
&cirrus_conn_funcs, DRM_MODE_CONNECTOR_VGA);
}
/* ------------------------------------------------------------------ */
/* cirrus (simple) display pipe */
static enum drm_mode_status cirrus_pipe_mode_valid(struct drm_simple_display_pipe *pipe,
const struct drm_display_mode *mode)
{
if (cirrus_check_size(mode->hdisplay, mode->vdisplay, NULL) < 0)
return MODE_BAD;
return MODE_OK;
}
static int cirrus_pipe_check(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *plane_state,
struct drm_crtc_state *crtc_state)
{
struct drm_framebuffer *fb = plane_state->fb;
if (!fb)
return 0;
return cirrus_check_size(fb->width, fb->height, fb);
}
static void cirrus_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state,
struct drm_plane_state *plane_state)
{
struct cirrus_device *cirrus = to_cirrus(pipe->crtc.dev);
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
cirrus_mode_set(cirrus, &crtc_state->mode, plane_state->fb);
cirrus_fb_blit_fullscreen(plane_state->fb, &shadow_plane_state->data[0]);
}
static void cirrus_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state)
{
struct cirrus_device *cirrus = to_cirrus(pipe->crtc.dev);
struct drm_plane_state *state = pipe->plane.state;
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
struct drm_crtc *crtc = &pipe->crtc;
struct drm_rect rect;
if (state->fb && cirrus->cpp != cirrus_cpp(state->fb))
cirrus_mode_set(cirrus, &crtc->mode, state->fb);
if (state->fb && drm_atomic_helper_damage_merged(old_state, state, &rect))
cirrus_fb_blit_rect(state->fb, &shadow_plane_state->data[0], &rect);
}
static const struct drm_simple_display_pipe_funcs cirrus_pipe_funcs = {
.mode_valid = cirrus_pipe_mode_valid,
.check = cirrus_pipe_check,
.enable = cirrus_pipe_enable,
.update = cirrus_pipe_update,
DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS,
};
static const uint32_t cirrus_formats[] = {
DRM_FORMAT_RGB565,
DRM_FORMAT_RGB888,
DRM_FORMAT_XRGB8888,
};
static const uint64_t cirrus_modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID
};
static int cirrus_pipe_init(struct cirrus_device *cirrus)
{
return drm_simple_display_pipe_init(&cirrus->dev,
&cirrus->pipe,
&cirrus_pipe_funcs,
cirrus_formats,
ARRAY_SIZE(cirrus_formats),
cirrus_modifiers,
&cirrus->conn);
struct drm_device *dev = &cirrus->dev;
struct drm_plane *primary_plane;
struct drm_crtc *crtc;
struct drm_encoder *encoder;
struct drm_connector *connector;
int ret;
primary_plane = &cirrus->primary_plane;
ret = drm_universal_plane_init(dev, primary_plane, 0,
&cirrus_primary_plane_funcs,
cirrus_primary_plane_formats,
ARRAY_SIZE(cirrus_primary_plane_formats),
cirrus_primary_plane_format_modifiers,
DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret)
return ret;
drm_plane_helper_add(primary_plane, &cirrus_primary_plane_helper_funcs);
drm_plane_enable_fb_damage_clips(primary_plane);
crtc = &cirrus->crtc;
ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
&cirrus_crtc_funcs, NULL);
if (ret)
return ret;
drm_crtc_helper_add(crtc, &cirrus_crtc_helper_funcs);
encoder = &cirrus->encoder;
ret = drm_encoder_init(dev, encoder, &cirrus_encoder_funcs,
DRM_MODE_ENCODER_DAC, NULL);
if (ret)
return ret;
encoder->possible_crtcs = drm_crtc_mask(crtc);
connector = &cirrus->connector;
ret = drm_connector_init(dev, connector, &cirrus_connector_funcs,
DRM_MODE_CONNECTOR_VGA);
if (ret)
return ret;
drm_connector_helper_add(connector, &cirrus_connector_helper_funcs);
ret = drm_connector_attach_encoder(connector, encoder);
if (ret)
return ret;
return 0;
}
/* ------------------------------------------------------------------ */
/* cirrus framebuffers & mode config */
static struct drm_framebuffer*
cirrus_fb_create(struct drm_device *dev, struct drm_file *file_priv,
const struct drm_mode_fb_cmd2 *mode_cmd)
static enum drm_mode_status cirrus_mode_config_mode_valid(struct drm_device *dev,
const struct drm_display_mode *mode)
{
if (mode_cmd->pixel_format != DRM_FORMAT_RGB565 &&
mode_cmd->pixel_format != DRM_FORMAT_RGB888 &&
mode_cmd->pixel_format != DRM_FORMAT_XRGB8888)
return ERR_PTR(-EINVAL);
if (cirrus_check_size(mode_cmd->width, mode_cmd->height, NULL) < 0)
return ERR_PTR(-EINVAL);
return drm_gem_fb_create_with_dirty(dev, file_priv, mode_cmd);
const struct drm_format_info *format = drm_format_info(DRM_FORMAT_XRGB8888);
uint64_t pitch = drm_format_info_min_pitch(format, 0, mode->hdisplay);
if (pitch * mode->vdisplay > CIRRUS_VRAM_SIZE)
return MODE_MEM;
return MODE_OK;
}
static const struct drm_mode_config_funcs cirrus_mode_config_funcs = {
.fb_create = cirrus_fb_create,
.fb_create = drm_gem_fb_create_with_dirty,
.mode_valid = cirrus_mode_config_mode_valid,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
@ -589,10 +704,6 @@ static int cirrus_pci_probe(struct pci_dev *pdev,
if (ret)
return ret;
ret = cirrus_conn_init(cirrus);
if (ret < 0)
return ret;
ret = cirrus_pipe_init(cirrus);
if (ret < 0)
return ret;

View File

@ -464,7 +464,8 @@ bounce:
if (ret == -EMULTIHOP) {
ret = ttm_bo_bounce_temp_buffer(bo, &evict_mem, ctx, &hop);
if (ret) {
pr_err("Buffer eviction failed\n");
if (ret != -ERESTARTSYS && ret != -EINTR)
pr_err("Buffer eviction failed\n");
ttm_resource_free(bo, &evict_mem);
goto out;
}

View File

@ -137,7 +137,6 @@ int ttm_global_swapout(struct ttm_operation_ctx *ctx, gfp_t gfp_flags)
mutex_unlock(&ttm_global_mutex);
return ret;
}
EXPORT_SYMBOL(ttm_global_swapout);
int ttm_device_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx,
gfp_t gfp_flags)

View File

@ -40,7 +40,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_module.h>
@ -227,7 +227,7 @@ static int tve200_probe(struct platform_device *pdev)
* Passing in 16 here will make the RGB565 mode the default
* Passing in 32 will use XRGB8888 mode
*/
drm_fbdev_generic_setup(drm, 16);
drm_fbdev_dma_setup(drm, 16);
return 0;

View File

@ -33,7 +33,7 @@
#include <drm/drm_aperture.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_vblank.h>
#include <soc/bcm2835/raspberrypi-firmware.h>
@ -387,7 +387,7 @@ static int vc4_drm_bind(struct device *dev)
if (ret < 0)
goto unbind_all;
drm_fbdev_generic_setup(drm, 16);
drm_fbdev_dma_setup(drm, 16);
return 0;

View File

@ -390,5 +390,9 @@ struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev,
return plane;
drm_plane_helper_add(plane, funcs);
if (type == DRM_PLANE_TYPE_PRIMARY)
drm_plane_enable_fb_damage_clips(plane);
return plane;
}

View File

@ -241,7 +241,7 @@ static int vmw_debugfs_gem_info_show(struct seq_file *m, void *unused)
* Therefore, we need to protect this ->comm access using RCU.
*/
rcu_read_lock();
task = pid_task(file->pid, PIDTYPE_PID);
task = pid_task(file->pid, PIDTYPE_TGID);
seq_printf(m, "pid %8d command %s:\n", pid_nr(file->pid),
task ? task->comm : "<unknown>");
rcu_read_unlock();

View File

@ -506,11 +506,11 @@ static void vmw_stdu_bo_cpu_commit(struct vmw_kms_dirty *dirty)
/* Assume we are blitting from Guest (bo) to Host (display_srf) */
src_pitch = stdu->display_srf->metadata.base_size.width * stdu->cpp;
src_bo = &stdu->display_srf->res.guest_memory_bo->tbo;
src_offset = ddirty->top * dst_pitch + ddirty->left * stdu->cpp;
src_offset = ddirty->top * src_pitch + ddirty->left * stdu->cpp;
dst_pitch = ddirty->pitch;
dst_bo = &ddirty->buf->tbo;
dst_offset = ddirty->fb_top * src_pitch + ddirty->fb_left * stdu->cpp;
dst_offset = ddirty->fb_top * dst_pitch + ddirty->fb_left * stdu->cpp;
(void) vmw_bo_cpu_blit(dst_bo, dst_offset, dst_pitch,
src_bo, src_offset, src_pitch,

View File

@ -19,7 +19,7 @@
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_encoder.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
@ -515,7 +515,7 @@ int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
goto err_poll_fini;
/* Initialize fbdev generic emulation. */
drm_fbdev_generic_setup(drm, 24);
drm_fbdev_dma_setup(drm, 24);
return 0;

View File

@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
config IMX_IPUV3_CORE
tristate "IPUv3 core support"
depends on SOC_IMX5 || SOC_IMX6Q || ARCH_MULTIPLATFORM || COMPILE_TEST
depends on SOC_IMX5 || SOC_IMX6Q || COMPILE_TEST
depends on DRM || !DRM # if DRM=m, this can't be 'y'
select BITREVERSE
select GENERIC_ALLOCATOR if DRM

View File

@ -0,0 +1,15 @@
/* SPDX-License-Identifier: MIT */
#ifndef DRM_FBDEV_DMA_H
#define DRM_FBDEV_DMA_H
struct drm_device;
#ifdef CONFIG_DRM_FBDEV_EMULATION
void drm_fbdev_dma_setup(struct drm_device *dev, unsigned int preferred_bpp);
#else
static inline void drm_fbdev_dma_setup(struct drm_device *dev, unsigned int preferred_bpp)
{ }
#endif
#endif

View File

@ -83,12 +83,12 @@ struct ttm_tt {
* set by TTM after ttm_tt_populate() has successfully returned, and is
* then unset when TTM calls ttm_tt_unpopulate().
*/
#define TTM_TT_FLAG_SWAPPED (1 << 0)
#define TTM_TT_FLAG_ZERO_ALLOC (1 << 1)
#define TTM_TT_FLAG_EXTERNAL (1 << 2)
#define TTM_TT_FLAG_EXTERNAL_MAPPABLE (1 << 3)
#define TTM_TT_FLAG_SWAPPED BIT(0)
#define TTM_TT_FLAG_ZERO_ALLOC BIT(1)
#define TTM_TT_FLAG_EXTERNAL BIT(2)
#define TTM_TT_FLAG_EXTERNAL_MAPPABLE BIT(3)
#define TTM_TT_FLAG_PRIV_POPULATED (1U << 31)
#define TTM_TT_FLAG_PRIV_POPULATED BIT(4)
uint32_t page_flags;
/** @num_pages: Number of pages in the page array. */
uint32_t num_pages;