mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-15 15:04:27 +08:00
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:
commit
c6265f5c2f
@ -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";
|
||||
|
@ -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
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
@ -25,6 +25,8 @@ properties:
|
||||
avdd-supply:
|
||||
description: 5v analog regulator
|
||||
|
||||
enable-gpios: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- dvdd-supply
|
||||
|
@ -28,6 +28,7 @@ properties:
|
||||
items:
|
||||
- enum:
|
||||
- densitron,dmt028vghmcmi-1a
|
||||
- elida,kd50t048a
|
||||
- techstar,ts8550b
|
||||
- const: sitronix,st7701
|
||||
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
@ -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>
|
||||
|
@ -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/
|
||||
|
@ -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
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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>",
|
||||
|
275
drivers/gpu/drm/drm_fbdev_dma.c
Normal file
275
drivers/gpu/drm/drm_fbdev_dma.c
Normal 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);
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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"
|
||||
|
@ -2,3 +2,4 @@
|
||||
|
||||
obj-$(CONFIG_DRM_IMX_DCSS) += dcss/
|
||||
obj-$(CONFIG_DRM_IMX) += ipuv3/
|
||||
obj-$(CONFIG_DRM_IMX_LCDC) += lcdc/
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
7
drivers/gpu/drm/imx/lcdc/Kconfig
Normal file
7
drivers/gpu/drm/imx/lcdc/Kconfig
Normal 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.
|
1
drivers/gpu/drm/imx/lcdc/Makefile
Normal file
1
drivers/gpu/drm/imx/lcdc/Makefile
Normal file
@ -0,0 +1 @@
|
||||
obj-$(CONFIG_DRM_IMX_LCDC) += imx-lcdc.o
|
546
drivers/gpu/drm/imx/lcdc/imx-lcdc.c
Normal file
546
drivers/gpu/drm/imx/lcdc/imx-lcdc.c
Normal 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");
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
777
drivers/gpu/drm/panel/panel-novatek-nt36523.c
Normal file
777
drivers/gpu/drm/panel/panel-novatek-nt36523.c
Normal 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");
|
@ -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);
|
||||
|
||||
|
@ -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 },
|
||||
{ }
|
||||
};
|
||||
|
329
drivers/gpu/drm/panel/panel-sony-td4353-jdi.c
Normal file
329
drivers/gpu/drm/panel/panel-sony-td4353-jdi.c
Normal 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");
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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[] = {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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__);
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
15
include/drm/drm_fbdev_dma.h
Normal file
15
include/drm/drm_fbdev_dma.h
Normal 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
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user