Merge tag 'drm-misc-next-2024-09-20' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next

drm-misc-next for v6.12:

UAPI Changes:
- Add panthor/DEV_QUERY_TIMESTAMP_INFO query.

Cross-subsystem Changes:
- Updated dt bindings.
- Add documentation explaining default errnos for fences.
- Mark dma-buf heaps creation functions as __init.

Core Changes:
- Split DSC helpers from DP helpers.
- Clang build fixes for drm/mm test.
- Remove simple pipeline support for gem-vram,
  no longer any users left after converting bochs.
- Add erno to drm_sched_start to distinguish between GPU and queue
  reset.
- Add drm_framebuffer testcases.
- Fix uninitialized spinlock acquisition with CONFIG_DRM_PANIC=n.
- Use read_trylock instead of read_lock in dma_fence_begin_signalling to
  quiesce lockdep.

Driver Changes:
- Assorted small fixes and updates for tegra, host1x, imagination,
  nouveau, panfrost, panthor, panel/ili9341, mali, exynos,
  panel/samsung-s6e3fa7, ast, bridge/ti-sn65dsi86, panel/himax-hx83112a,
  bridge/tc358767, bridge/imx8mp-hdmi-tx, panel/khadas-ts050,
  panel/nt36523, panel/sony-acx565akm, kmb, accel/qaic, omap, v3d.
- Add bridge/TI TDP158.
- Assorted documentation updates.
- Convert bochs from simple drm to gem shmem, and check modes
  against available memory.
- Many VC4 fixes, most related to scaling and YUV support.
- Convert some drivers to use SYSTEM_SLEEP_PM_OPS and RUNTIME_PM_OPS.
- Rockchip 4k@60 support.

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/445713a6-2427-4c53-8ec2-3a894ec62405@linux.intel.com
This commit is contained in:
Dave Airlie 2024-10-09 09:03:45 +10:00
commit 7fefa1edc2
136 changed files with 2764 additions and 2130 deletions

View File

@ -0,0 +1,57 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/bridge/ti,tdp158.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI TDP158 HDMI to TMDS Redriver
maintainers:
- Arnaud Vrac <avrac@freebox.fr>
- Pierre-Hugues Husson <phhusson@freebox.fr>
properties:
compatible:
const: ti,tdp158
# The reg property is required if and only if the device is connected
# to an I2C bus. In pin strap mode, reg must not be specified.
reg:
description: I2C address of the device
# Pin 36 = Operation Enable / Reset Pin
# OE = L: Power Down Mode
# OE = H: Normal Operation
# Internal weak pullup - device resets on H to L transitions
enable-gpios:
description: GPIO controlling bridge enable
vcc-supply:
description: Power supply 3.3V
vdd-supply:
description: Power supply 1.1V
ports:
$ref: /schemas/graph.yaml#/properties/ports
properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description: Bridge input
port@1:
$ref: /schemas/graph.yaml#/properties/port
description: Bridge output
required:
- port@0
- port@1
required:
- compatible
- vcc-supply
- vdd-supply
- ports
additionalProperties: false

View File

@ -119,7 +119,6 @@ Optional properties:
- interface-pix-fmt: How this display is connected to the
display interface. Currently supported types: "rgb24", "rgb565", "bgr666"
and "lvds666".
- edid: verbatim EDID data block describing attached display.
- ddc: phandle describing the i2c bus handling the display data
channel
- port@[0-1]: Port nodes with endpoint definitions as defined in
@ -131,7 +130,6 @@ example:
disp0 {
compatible = "fsl,imx-parallel-display";
edid = [edid-data];
interface-pix-fmt = "rgb24";
port@0 {

View File

@ -62,7 +62,6 @@ Required properties:
display-timings are used instead.
Optional properties (required if display-timings are used):
- ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
- display-timings : A node that describes the display timings as defined in
Documentation/devicetree/bindings/display/panel/display-timing.txt.
- fsl,data-mapping : should be "spwg" or "jeida"

View File

@ -26,6 +26,7 @@ properties:
- renesas,r9a07g054-mali
- rockchip,px30-mali
- rockchip,rk3568-mali
- rockchip,rk3576-mali
- const: arm,mali-bifrost # Mali Bifrost GPU model/revision is fully discoverable
- items:
- enum:

View File

@ -305,13 +305,26 @@ Kernel Mode Driver
------------------
The KMD is responsible for checking if the device needs a reset, and to perform
it as needed. Usually a hang is detected when a job gets stuck executing. KMD
should keep track of resets, because userspace can query any time about the
reset status for a specific context. This is needed to propagate to the rest of
the stack that a reset has happened. Currently, this is implemented by each
driver separately, with no common DRM interface. Ideally this should be properly
integrated at DRM scheduler to provide a common ground for all drivers. After a
reset, KMD should reject new command submissions for affected contexts.
it as needed. Usually a hang is detected when a job gets stuck executing.
Propagation of errors to userspace has proven to be tricky since it goes in
the opposite direction of the usual flow of commands. Because of this vendor
independent error handling was added to the &dma_fence object, this way drivers
can add an error code to their fences before signaling them. See function
dma_fence_set_error() on how to do this and for examples of error codes to use.
The DRM scheduler also allows setting error codes on all pending fences when
hardware submissions are restarted after an reset. Error codes are also
forwarded from the hardware fence to the scheduler fence to bubble up errors
to the higher levels of the stack and eventually userspace.
Fence errors can be queried by userspace through the generic SYNC_IOC_FILE_INFO
IOCTL as well as through driver specific interfaces.
Additional to setting fence errors drivers should also keep track of resets per
context, the DRM scheduler provides the drm_sched_entity_error() function as
helper for this use case. After a reset, KMD should reject new command
submissions for affected contexts.
User Mode Driver
----------------

View File

@ -834,6 +834,22 @@ Contact: Javier Martinez Canillas <javierm@redhat.com>
Level: Advanced
Querying errors from drm_syncobj
================================
The drm_syncobj container can be used by driver independent code to signal
complection of submission.
One minor feature still missing is a generic DRM IOCTL to query the error
status of binary and timeline drm_syncobj.
This should probably be improved by implementing the necessary kernel interface
and adding support for that in the userspace stack.
Contact: Christian König
Level: Starter
Outside DRM
===========

View File

@ -64,20 +64,9 @@ static int bootlog_show(struct seq_file *s, void *unused)
return 0;
}
static int bootlog_fops_open(struct inode *inode, struct file *file)
{
return single_open(file, bootlog_show, inode->i_private);
}
DEFINE_SHOW_ATTRIBUTE(bootlog);
static const struct file_operations bootlog_fops = {
.owner = THIS_MODULE,
.open = bootlog_fops_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int read_dbc_fifo_size(struct seq_file *s, void *unused)
static int fifo_size_show(struct seq_file *s, void *unused)
{
struct dma_bridge_chan *dbc = s->private;
@ -85,20 +74,9 @@ static int read_dbc_fifo_size(struct seq_file *s, void *unused)
return 0;
}
static int fifo_size_open(struct inode *inode, struct file *file)
{
return single_open(file, read_dbc_fifo_size, inode->i_private);
}
DEFINE_SHOW_ATTRIBUTE(fifo_size);
static const struct file_operations fifo_size_fops = {
.owner = THIS_MODULE,
.open = fifo_size_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int read_dbc_queued(struct seq_file *s, void *unused)
static int queued_show(struct seq_file *s, void *unused)
{
struct dma_bridge_chan *dbc = s->private;
u32 tail = 0, head = 0;
@ -115,18 +93,7 @@ static int read_dbc_queued(struct seq_file *s, void *unused)
return 0;
}
static int queued_open(struct inode *inode, struct file *file)
{
return single_open(file, read_dbc_queued, inode->i_private);
}
static const struct file_operations queued_fops = {
.owner = THIS_MODULE,
.open = queued_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
DEFINE_SHOW_ATTRIBUTE(queued);
void qaic_debugfs_init(struct qaic_drm_device *qddev)
{

View File

@ -309,8 +309,8 @@ bool dma_fence_begin_signalling(void)
if (in_atomic())
return true;
/* ... and non-recursive readlock */
lock_acquire(&dma_fence_lockdep_map, 0, 0, 1, 1, NULL, _RET_IP_);
/* ... and non-recursive successful read_trylock */
lock_acquire(&dma_fence_lockdep_map, 0, 1, 1, 1, NULL, _RET_IP_);
return false;
}
@ -341,7 +341,7 @@ void __dma_fence_might_wait(void)
lock_map_acquire(&dma_fence_lockdep_map);
lock_map_release(&dma_fence_lockdep_map);
if (tmp)
lock_acquire(&dma_fence_lockdep_map, 0, 0, 1, 1, NULL, _THIS_IP_);
lock_acquire(&dma_fence_lockdep_map, 0, 1, 1, 1, NULL, _THIS_IP_);
}
#endif

View File

@ -366,7 +366,7 @@ static const struct dma_heap_ops cma_heap_ops = {
.allocate = cma_heap_allocate,
};
static int __add_cma_heap(struct cma *cma, void *data)
static int __init __add_cma_heap(struct cma *cma, void *data)
{
struct cma_heap *cma_heap;
struct dma_heap_export_info exp_info;
@ -391,7 +391,7 @@ static int __add_cma_heap(struct cma *cma, void *data)
return 0;
}
static int add_default_cma_heap(void)
static int __init add_default_cma_heap(void)
{
struct cma *default_cma = dev_get_cma_area(NULL);
int ret = 0;

View File

@ -421,7 +421,7 @@ static const struct dma_heap_ops system_heap_ops = {
.allocate = system_heap_allocate,
};
static int system_heap_create(void)
static int __init system_heap_create(void)
{
struct dma_heap_export_info exp_info;

View File

@ -6,6 +6,7 @@ config DRM_AMDGPU
depends on !UML
select FW_LOADER
select DRM_DISPLAY_DP_HELPER
select DRM_DISPLAY_DSC_HELPER
select DRM_DISPLAY_HDMI_HELPER
select DRM_DISPLAY_HDCP_HELPER
select DRM_DISPLAY_HELPER

View File

@ -299,7 +299,7 @@ static int suspend_resume_compute_scheduler(struct amdgpu_device *adev, bool sus
if (r)
goto out;
} else {
drm_sched_start(&ring->sched);
drm_sched_start(&ring->sched, 0);
}
}

View File

@ -5824,7 +5824,7 @@ skip_hw_reset:
if (!amdgpu_ring_sched_ready(ring))
continue;
drm_sched_start(&ring->sched);
drm_sched_start(&ring->sched, 0);
}
if (!drm_drv_uses_atomic_modeset(adev_to_drm(tmp_adev)) && !job_signaled)
@ -6331,7 +6331,7 @@ void amdgpu_pci_resume(struct pci_dev *pdev)
if (!amdgpu_ring_sched_ready(ring))
continue;
drm_sched_start(&ring->sched);
drm_sched_start(&ring->sched, 0);
}
amdgpu_device_unset_mp1_state(adev);

View File

@ -149,7 +149,7 @@ static enum drm_gpu_sched_stat amdgpu_job_timedout(struct drm_sched_job *s_job)
atomic_inc(&ring->adev->gpu_reset_counter);
amdgpu_fence_driver_force_completion(ring);
if (amdgpu_ring_sched_ready(ring))
drm_sched_start(&ring->sched);
drm_sched_start(&ring->sched, 0);
goto exit;
}
}

View File

@ -149,28 +149,22 @@ int ast_dp_launch(struct ast_device *ast)
return 0;
}
static bool ast_dp_power_is_on(struct ast_device *ast)
static bool ast_dp_get_phy_sleep(struct ast_device *ast)
{
u8 vgacre3;
u8 vgacre3 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xe3);
vgacre3 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xe3);
return !(vgacre3 & AST_DP_PHY_SLEEP);
return (vgacre3 & AST_IO_VGACRE3_DP_PHY_SLEEP);
}
static void ast_dp_power_on_off(struct drm_device *dev, bool on)
static void ast_dp_set_phy_sleep(struct ast_device *ast, bool sleep)
{
struct ast_device *ast = to_ast_device(dev);
// Read and Turn off DP PHY sleep
u8 bE3 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xE3, AST_DP_VIDEO_ENABLE);
u8 vgacre3 = 0x00;
// Turn on DP PHY sleep
if (!on)
bE3 |= AST_DP_PHY_SLEEP;
// DP Power on/off
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xE3, (u8) ~AST_DP_PHY_SLEEP, bE3);
if (sleep)
vgacre3 |= AST_IO_VGACRE3_DP_PHY_SLEEP;
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xe3, (u8)~AST_IO_VGACRE3_DP_PHY_SLEEP,
vgacre3);
msleep(50);
}
@ -192,23 +186,39 @@ static void ast_dp_link_training(struct ast_device *ast)
drm_err(dev, "Link training failed\n");
}
static void ast_dp_set_on_off(struct drm_device *dev, bool on)
static bool __ast_dp_wait_enable(struct ast_device *ast, bool enabled)
{
struct ast_device *ast = to_ast_device(dev);
u8 video_on_off = on;
u32 i = 0;
u8 vgacrdf_test = 0x00;
u8 vgacrdf;
unsigned int i;
// Video On/Off
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xE3, (u8) ~AST_DP_VIDEO_ENABLE, on);
if (enabled)
vgacrdf_test |= AST_IO_VGACRDF_DP_VIDEO_ENABLE;
video_on_off <<= 4;
while (ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xDF,
ASTDP_MIRROR_VIDEO_ENABLE) != video_on_off) {
// wait 1 ms
mdelay(1);
if (++i > 200)
break;
for (i = 0; i < 200; ++i) {
if (i)
mdelay(1);
vgacrdf = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xdf,
AST_IO_VGACRDF_DP_VIDEO_ENABLE);
if (vgacrdf == vgacrdf_test)
return true;
}
return false;
}
static void ast_dp_set_enable(struct ast_device *ast, bool enabled)
{
struct drm_device *dev = &ast->base;
u8 vgacre3 = 0x00;
if (enabled)
vgacre3 |= AST_IO_VGACRE3_DP_VIDEO_ENABLE;
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xe3, (u8)~AST_IO_VGACRE3_DP_VIDEO_ENABLE,
vgacre3);
drm_WARN_ON(dev, !__ast_dp_wait_enable(ast, enabled));
}
static void ast_dp_set_mode(struct drm_crtc *crtc, struct ast_vbios_mode_info *vbios_mode)
@ -317,26 +327,25 @@ static void ast_astdp_encoder_helper_atomic_mode_set(struct drm_encoder *encoder
static void ast_astdp_encoder_helper_atomic_enable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct drm_device *dev = encoder->dev;
struct ast_device *ast = to_ast_device(dev);
struct ast_device *ast = to_ast_device(encoder->dev);
struct ast_connector *ast_connector = &ast->output.astdp.connector;
if (ast_connector->physical_status == connector_status_connected) {
ast_dp_power_on_off(dev, AST_DP_POWER_ON);
ast_dp_set_phy_sleep(ast, false);
ast_dp_link_training(ast);
ast_wait_for_vretrace(ast);
ast_dp_set_on_off(dev, 1);
ast_dp_set_enable(ast, true);
}
}
static void ast_astdp_encoder_helper_atomic_disable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct drm_device *dev = encoder->dev;
struct ast_device *ast = to_ast_device(encoder->dev);
ast_dp_set_on_off(dev, 0);
ast_dp_power_on_off(dev, AST_DP_POWER_OFF);
ast_dp_set_enable(ast, false);
ast_dp_set_phy_sleep(ast, true);
}
static const struct drm_encoder_helper_funcs ast_astdp_encoder_helper_funcs = {
@ -383,22 +392,21 @@ static int ast_astdp_connector_helper_detect_ctx(struct drm_connector *connector
bool force)
{
struct ast_connector *ast_connector = to_ast_connector(connector);
struct drm_device *dev = connector->dev;
struct ast_device *ast = to_ast_device(connector->dev);
enum drm_connector_status status = connector_status_disconnected;
bool power_is_on;
bool phy_sleep;
mutex_lock(&ast->modeset_lock);
power_is_on = ast_dp_power_is_on(ast);
if (!power_is_on)
ast_dp_power_on_off(dev, true);
phy_sleep = ast_dp_get_phy_sleep(ast);
if (phy_sleep)
ast_dp_set_phy_sleep(ast, false);
if (ast_astdp_is_connected(ast))
status = connector_status_connected;
if (!power_is_on && status == connector_status_disconnected)
ast_dp_power_on_off(dev, false);
if (phy_sleep && status == connector_status_disconnected)
ast_dp_set_phy_sleep(ast, true);
mutex_unlock(&ast->modeset_lock);
@ -414,6 +422,10 @@ static const struct drm_connector_helper_funcs ast_astdp_connector_helper_funcs
.detect_ctx = ast_astdp_connector_helper_detect_ctx,
};
/*
* Output
*/
static const struct drm_connector_funcs ast_astdp_connector_funcs = {
.reset = drm_atomic_helper_connector_reset,
.fill_modes = drm_helper_probe_single_connector_modes,
@ -422,34 +434,18 @@ static const struct drm_connector_funcs ast_astdp_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int ast_astdp_connector_init(struct drm_device *dev, struct drm_connector *connector)
{
int ret;
ret = drm_connector_init(dev, connector, &ast_astdp_connector_funcs,
DRM_MODE_CONNECTOR_DisplayPort);
if (ret)
return ret;
drm_connector_helper_add(connector, &ast_astdp_connector_helper_funcs);
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
return 0;
}
int ast_astdp_output_init(struct ast_device *ast)
{
struct drm_device *dev = &ast->base;
struct drm_crtc *crtc = &ast->crtc;
struct drm_encoder *encoder = &ast->output.astdp.encoder;
struct ast_connector *ast_connector = &ast->output.astdp.connector;
struct drm_connector *connector = &ast_connector->base;
struct drm_encoder *encoder;
struct ast_connector *ast_connector;
struct drm_connector *connector;
int ret;
/* encoder */
encoder = &ast->output.astdp.encoder;
ret = drm_encoder_init(dev, encoder, &ast_astdp_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL);
if (ret)
@ -458,9 +454,20 @@ int ast_astdp_output_init(struct ast_device *ast)
encoder->possible_crtcs = drm_crtc_mask(crtc);
ret = ast_astdp_connector_init(dev, connector);
/* connector */
ast_connector = &ast->output.astdp.connector;
connector = &ast_connector->base;
ret = drm_connector_init(dev, connector, &ast_astdp_connector_funcs,
DRM_MODE_CONNECTOR_DisplayPort);
if (ret)
return ret;
drm_connector_helper_add(connector, &ast_astdp_connector_helper_funcs);
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
ast_connector->physical_status = connector->status;
ret = drm_connector_attach_encoder(connector, encoder);

View File

@ -21,9 +21,9 @@ static void ast_release_firmware(void *data)
ast->dp501_fw = NULL;
}
static int ast_load_dp501_microcode(struct drm_device *dev)
static int ast_load_dp501_microcode(struct ast_device *ast)
{
struct ast_device *ast = to_ast_device(dev);
struct drm_device *dev = &ast->base;
int ret;
ret = request_firmware(&ast->dp501_fw, "ast_dp501_fw.bin", dev->dev);
@ -109,10 +109,10 @@ static bool wait_fw_ready(struct ast_device *ast)
}
#endif
static bool ast_write_cmd(struct drm_device *dev, u8 data)
static bool ast_write_cmd(struct ast_device *ast, u8 data)
{
struct ast_device *ast = to_ast_device(dev);
int retry = 0;
if (wait_nack(ast)) {
send_nack(ast);
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9a, 0x00, data);
@ -131,10 +131,8 @@ static bool ast_write_cmd(struct drm_device *dev, u8 data)
return false;
}
static bool ast_write_data(struct drm_device *dev, u8 data)
static bool ast_write_data(struct ast_device *ast, u8 data)
{
struct ast_device *ast = to_ast_device(dev);
if (wait_nack(ast)) {
send_nack(ast);
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9a, 0x00, data);
@ -175,10 +173,10 @@ static void clear_cmd(struct ast_device *ast)
}
#endif
static void ast_set_dp501_video_output(struct drm_device *dev, u8 mode)
static void ast_set_dp501_video_output(struct ast_device *ast, u8 mode)
{
ast_write_cmd(dev, 0x40);
ast_write_data(dev, mode);
ast_write_cmd(ast, 0x40);
ast_write_data(ast, mode);
msleep(10);
}
@ -188,9 +186,8 @@ static u32 get_fw_base(struct ast_device *ast)
return ast_mindwm(ast, 0x1e6e2104) & 0x7fffffff;
}
bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size)
bool ast_backup_fw(struct ast_device *ast, u8 *addr, u32 size)
{
struct ast_device *ast = to_ast_device(dev);
u32 i, data;
u32 boot_address;
@ -207,9 +204,8 @@ bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size)
return false;
}
static bool ast_launch_m68k(struct drm_device *dev)
static bool ast_launch_m68k(struct ast_device *ast)
{
struct ast_device *ast = to_ast_device(dev);
u32 i, data, len = 0;
u32 boot_address;
u8 *fw_addr = NULL;
@ -226,7 +222,7 @@ static bool ast_launch_m68k(struct drm_device *dev)
len = 32*1024;
} else {
if (!ast->dp501_fw &&
ast_load_dp501_microcode(dev) < 0)
ast_load_dp501_microcode(ast) < 0)
return false;
fw_addr = (u8 *)ast->dp501_fw->data;
@ -348,9 +344,8 @@ static int ast_dp512_read_edid_block(void *data, u8 *buf, unsigned int block, si
return true;
}
static bool ast_init_dvo(struct drm_device *dev)
static bool ast_init_dvo(struct ast_device *ast)
{
struct ast_device *ast = to_ast_device(dev);
u8 jreg;
u32 data;
ast_write32(ast, 0xf004, 0x1e6e0000);
@ -421,9 +416,8 @@ static bool ast_init_dvo(struct drm_device *dev)
}
static void ast_init_analog(struct drm_device *dev)
static void ast_init_analog(struct ast_device *ast)
{
struct ast_device *ast = to_ast_device(dev);
u32 data;
/*
@ -448,28 +442,28 @@ static void ast_init_analog(struct drm_device *dev)
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x00);
}
void ast_init_3rdtx(struct drm_device *dev)
void ast_init_3rdtx(struct ast_device *ast)
{
struct ast_device *ast = to_ast_device(dev);
u8 jreg;
u8 vgacrd1;
if (IS_AST_GEN4(ast) || IS_AST_GEN5(ast)) {
jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd1, 0xff);
switch (jreg & 0x0e) {
case 0x04:
ast_init_dvo(dev);
vgacrd1 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd1,
AST_IO_VGACRD1_TX_TYPE_MASK);
switch (vgacrd1) {
case AST_IO_VGACRD1_TX_SIL164_VBIOS:
ast_init_dvo(ast);
break;
case 0x08:
ast_launch_m68k(dev);
case AST_IO_VGACRD1_TX_DP501_VBIOS:
ast_launch_m68k(ast);
break;
case 0x0c:
ast_init_dvo(dev);
case AST_IO_VGACRD1_TX_FW_EMBEDDED_FW:
ast_init_dvo(ast);
break;
default:
if (ast->tx_chip_types & BIT(AST_TX_SIL164))
ast_init_dvo(dev);
if (ast->tx_chip == AST_TX_SIL164)
ast_init_dvo(ast);
else
ast_init_analog(dev);
ast_init_analog(ast);
}
}
}
@ -485,17 +479,17 @@ static const struct drm_encoder_funcs ast_dp501_encoder_funcs = {
static void ast_dp501_encoder_helper_atomic_enable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct drm_device *dev = encoder->dev;
struct ast_device *ast = to_ast_device(encoder->dev);
ast_set_dp501_video_output(dev, 1);
ast_set_dp501_video_output(ast, 1);
}
static void ast_dp501_encoder_helper_atomic_disable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct drm_device *dev = encoder->dev;
struct ast_device *ast = to_ast_device(encoder->dev);
ast_set_dp501_video_output(dev, 0);
ast_set_dp501_video_output(ast, 0);
}
static const struct drm_encoder_helper_funcs ast_dp501_encoder_helper_funcs = {
@ -567,34 +561,22 @@ static const struct drm_connector_funcs ast_dp501_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int ast_dp501_connector_init(struct drm_device *dev, struct drm_connector *connector)
{
int ret;
ret = drm_connector_init(dev, connector, &ast_dp501_connector_funcs,
DRM_MODE_CONNECTOR_DisplayPort);
if (ret)
return ret;
drm_connector_helper_add(connector, &ast_dp501_connector_helper_funcs);
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
return 0;
}
/*
* Output
*/
int ast_dp501_output_init(struct ast_device *ast)
{
struct drm_device *dev = &ast->base;
struct drm_crtc *crtc = &ast->crtc;
struct drm_encoder *encoder = &ast->output.dp501.encoder;
struct ast_connector *ast_connector = &ast->output.dp501.connector;
struct drm_connector *connector = &ast_connector->base;
struct drm_encoder *encoder;
struct ast_connector *ast_connector;
struct drm_connector *connector;
int ret;
/* encoder */
encoder = &ast->output.dp501.encoder;
ret = drm_encoder_init(dev, encoder, &ast_dp501_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL);
if (ret)
@ -603,9 +585,20 @@ int ast_dp501_output_init(struct ast_device *ast)
encoder->possible_crtcs = drm_crtc_mask(crtc);
ret = ast_dp501_connector_init(dev, connector);
/* connector */
ast_connector = &ast->output.dp501.connector;
connector = &ast_connector->base;
ret = drm_connector_init(dev, connector, &ast_dp501_connector_funcs,
DRM_MODE_CONNECTOR_DisplayPort);
if (ret)
return ret;
drm_connector_helper_add(connector, &ast_dp501_connector_helper_funcs);
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
ast_connector->physical_status = connector->status;
ret = drm_connector_attach_encoder(connector, encoder);

View File

@ -396,7 +396,7 @@ static int ast_drm_thaw(struct drm_device *dev)
ast_enable_vga(ast->ioregs);
ast_open_key(ast->ioregs);
ast_enable_mmio(dev->dev, ast->ioregs);
ast_post_gpu(dev);
ast_post_gpu(ast);
return drm_mode_config_helper_resume(dev);
}

View File

@ -91,11 +91,6 @@ enum ast_tx_chip {
AST_TX_ASTDP,
};
#define AST_TX_NONE_BIT BIT(AST_TX_NONE)
#define AST_TX_SIL164_BIT BIT(AST_TX_SIL164)
#define AST_TX_DP501_BIT BIT(AST_TX_DP501)
#define AST_TX_ASTDP_BIT BIT(AST_TX_ASTDP)
enum ast_config_mode {
ast_use_p2a,
ast_use_dt,
@ -187,10 +182,12 @@ struct ast_device {
struct mutex modeset_lock; /* Protects access to modeset I/O registers in ioregs */
enum ast_tx_chip tx_chip;
struct ast_plane primary_plane;
struct ast_plane cursor_plane;
struct drm_crtc crtc;
struct {
union {
struct {
struct drm_encoder encoder;
struct ast_connector connector;
@ -211,7 +208,6 @@ struct ast_device {
bool support_wide_screen;
unsigned long tx_chip_types; /* bitfield of enum ast_chip_type */
u8 *dp501_fw_addr;
const struct firmware *dp501_fw; /* dp501 fw */
};
@ -407,9 +403,6 @@ int ast_mode_config_init(struct ast_device *ast);
#define AST_DP501_LINKRATE 0xf014
#define AST_DP501_EDID_DATA 0xf020
#define AST_DP_POWER_ON true
#define AST_DP_POWER_OFF false
/*
* ASTDP resoultion table:
* EX: ASTDP_A_B_C:
@ -453,7 +446,7 @@ int ast_mode_config_init(struct ast_device *ast);
int ast_mm_init(struct ast_device *ast);
/* ast post */
void ast_post_gpu(struct drm_device *dev);
void ast_post_gpu(struct ast_device *ast);
u32 ast_mindwm(struct ast_device *ast, u32 r);
void ast_moutdwm(struct ast_device *ast, u32 r, u32 v);
void ast_patch_ahb_2500(void __iomem *regs);
@ -462,8 +455,8 @@ int ast_vga_output_init(struct ast_device *ast);
int ast_sil164_output_init(struct ast_device *ast);
/* ast dp501 */
bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size);
void ast_init_3rdtx(struct drm_device *dev);
bool ast_backup_fw(struct ast_device *ast, u8 *addr, u32 size);
void ast_init_3rdtx(struct ast_device *ast);
int ast_dp501_output_init(struct ast_device *ast);
/* aspeed DP */

View File

@ -68,11 +68,33 @@ static void ast_detect_widescreen(struct ast_device *ast)
static void ast_detect_tx_chip(struct ast_device *ast, bool need_post)
{
static const char * const info_str[] = {
"analog VGA",
"Sil164 TMDS transmitter",
"DP501 DisplayPort transmitter",
"ASPEED DisplayPort transmitter",
};
struct drm_device *dev = &ast->base;
u8 jreg;
u8 jreg, vgacrd1;
/*
* Several of the listed TX chips are not explicitly supported
* by the ast driver. If these exist in real-world devices, they
* are most likely reported as VGA or SIL164 outputs. We warn here
* to get bug reports for these devices. If none come in for some
* time, we can begin to fail device probing on these values.
*/
vgacrd1 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd1, AST_IO_VGACRD1_TX_TYPE_MASK);
drm_WARN(dev, vgacrd1 == AST_IO_VGACRD1_TX_ITE66121_VBIOS,
"ITE IT66121 detected, 0x%x, Gen%lu\n", vgacrd1, AST_GEN(ast));
drm_WARN(dev, vgacrd1 == AST_IO_VGACRD1_TX_CH7003_VBIOS,
"Chrontel CH7003 detected, 0x%x, Gen%lu\n", vgacrd1, AST_GEN(ast));
drm_WARN(dev, vgacrd1 == AST_IO_VGACRD1_TX_ANX9807_VBIOS,
"Analogix ANX9807 detected, 0x%x, Gen%lu\n", vgacrd1, AST_GEN(ast));
/* Check 3rd Tx option (digital output afaik) */
ast->tx_chip_types |= AST_TX_NONE_BIT;
ast->tx_chip = AST_TX_NONE;
/*
* VGACRA3 Enhanced Color Mode Register, check if DVO is already
@ -85,7 +107,7 @@ static void ast_detect_tx_chip(struct ast_device *ast, bool need_post)
if (!need_post) {
jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xff);
if (jreg & 0x80)
ast->tx_chip_types = AST_TX_SIL164_BIT;
ast->tx_chip = AST_TX_SIL164;
}
if (IS_AST_GEN4(ast) || IS_AST_GEN5(ast) || IS_AST_GEN6(ast)) {
@ -94,49 +116,42 @@ static void ast_detect_tx_chip(struct ast_device *ast, bool need_post)
* the SOC scratch register #1 bits 11:8 (interestingly marked
* as "reserved" in the spec)
*/
jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd1, 0xff);
jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd1,
AST_IO_VGACRD1_TX_TYPE_MASK);
switch (jreg) {
case 0x04:
ast->tx_chip_types = AST_TX_SIL164_BIT;
case AST_IO_VGACRD1_TX_SIL164_VBIOS:
ast->tx_chip = AST_TX_SIL164;
break;
case 0x08:
case AST_IO_VGACRD1_TX_DP501_VBIOS:
ast->dp501_fw_addr = drmm_kzalloc(dev, 32*1024, GFP_KERNEL);
if (ast->dp501_fw_addr) {
/* backup firmware */
if (ast_backup_fw(dev, ast->dp501_fw_addr, 32*1024)) {
if (ast_backup_fw(ast, ast->dp501_fw_addr, 32*1024)) {
drmm_kfree(dev, ast->dp501_fw_addr);
ast->dp501_fw_addr = NULL;
}
}
fallthrough;
case 0x0c:
ast->tx_chip_types = AST_TX_DP501_BIT;
case AST_IO_VGACRD1_TX_FW_EMBEDDED_FW:
ast->tx_chip = AST_TX_DP501;
}
} else if (IS_AST_GEN7(ast)) {
if (ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xD1, TX_TYPE_MASK) ==
ASTDP_DPMCU_TX) {
if (ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd1, AST_IO_VGACRD1_TX_TYPE_MASK) ==
AST_IO_VGACRD1_TX_ASTDP) {
int ret = ast_dp_launch(ast);
if (!ret)
ast->tx_chip_types = AST_TX_ASTDP_BIT;
ast->tx_chip = AST_TX_ASTDP;
}
}
/* Print stuff for diagnostic purposes */
if (ast->tx_chip_types & AST_TX_NONE_BIT)
drm_info(dev, "Using analog VGA\n");
if (ast->tx_chip_types & AST_TX_SIL164_BIT)
drm_info(dev, "Using Sil164 TMDS transmitter\n");
if (ast->tx_chip_types & AST_TX_DP501_BIT)
drm_info(dev, "Using DP501 DisplayPort transmitter\n");
if (ast->tx_chip_types & AST_TX_ASTDP_BIT)
drm_info(dev, "Using ASPEED DisplayPort transmitter\n");
drm_info(dev, "Using %s\n", info_str[ast->tx_chip]);
}
static int ast_get_dram_info(struct drm_device *dev)
static int ast_get_dram_info(struct ast_device *ast)
{
struct drm_device *dev = &ast->base;
struct device_node *np = dev->dev->of_node;
struct ast_device *ast = to_ast_device(dev);
uint32_t mcr_cfg, mcr_scu_mpll, mcr_scu_strap;
uint32_t denum, num, div, ref_pll, dsel;
@ -278,7 +293,7 @@ struct drm_device *ast_device_create(struct pci_dev *pdev,
ast_detect_widescreen(ast);
ast_detect_tx_chip(ast, need_post);
ret = ast_get_dram_info(dev);
ret = ast_get_dram_info(ast);
if (ret)
return ERR_PTR(ret);
@ -286,7 +301,7 @@ struct drm_device *ast_device_create(struct pci_dev *pdev,
ast->mclk, ast->dram_type, ast->dram_bus_width);
if (need_post)
ast_post_gpu(dev);
ast_post_gpu(ast);
ret = ast_mm_init(ast);
if (ret)

View File

@ -1287,9 +1287,9 @@ static const struct drm_crtc_funcs ast_crtc_funcs = {
.atomic_destroy_state = ast_crtc_atomic_destroy_state,
};
static int ast_crtc_init(struct drm_device *dev)
static int ast_crtc_init(struct ast_device *ast)
{
struct ast_device *ast = to_ast_device(dev);
struct drm_device *dev = &ast->base;
struct drm_crtc *crtc = &ast->crtc;
int ret;
@ -1396,28 +1396,26 @@ int ast_mode_config_init(struct ast_device *ast)
if (ret)
return ret;
ast_crtc_init(dev);
ret = ast_crtc_init(ast);
if (ret)
return ret;
if (ast->tx_chip_types & AST_TX_NONE_BIT) {
switch (ast->tx_chip) {
case AST_TX_NONE:
ret = ast_vga_output_init(ast);
if (ret)
return ret;
}
if (ast->tx_chip_types & AST_TX_SIL164_BIT) {
break;
case AST_TX_SIL164:
ret = ast_sil164_output_init(ast);
if (ret)
return ret;
}
if (ast->tx_chip_types & AST_TX_DP501_BIT) {
break;
case AST_TX_DP501:
ret = ast_dp501_output_init(ast);
if (ret)
return ret;
}
if (ast->tx_chip_types & AST_TX_ASTDP_BIT) {
break;
case AST_TX_ASTDP:
ret = ast_astdp_output_init(ast);
if (ret)
return ret;
break;
}
if (ret)
return ret;
drm_mode_config_reset(dev);

View File

@ -34,16 +34,14 @@
#include "ast_dram_tables.h"
#include "ast_drv.h"
static void ast_post_chip_2300(struct drm_device *dev);
static void ast_post_chip_2500(struct drm_device *dev);
static void ast_post_chip_2300(struct ast_device *ast);
static void ast_post_chip_2500(struct ast_device *ast);
static const u8 extreginfo[] = { 0x0f, 0x04, 0x1c, 0xff };
static const u8 extreginfo_ast2300[] = { 0x0f, 0x04, 0x1f, 0xff };
static void
ast_set_def_ext_reg(struct drm_device *dev)
static void ast_set_def_ext_reg(struct ast_device *ast)
{
struct ast_device *ast = to_ast_device(dev);
u8 i, index, reg;
const u8 *ext_reg_info;
@ -252,9 +250,8 @@ cbr_start:
static void ast_init_dram_reg(struct drm_device *dev)
static void ast_init_dram_reg(struct ast_device *ast)
{
struct ast_device *ast = to_ast_device(dev);
u8 j;
u32 data, temp, i;
const struct ast_dramstruct *dram_reg_info;
@ -343,26 +340,24 @@ static void ast_init_dram_reg(struct drm_device *dev)
} while ((j & 0x40) == 0);
}
void ast_post_gpu(struct drm_device *dev)
void ast_post_gpu(struct ast_device *ast)
{
struct ast_device *ast = to_ast_device(dev);
ast_set_def_ext_reg(dev);
ast_set_def_ext_reg(ast);
if (IS_AST_GEN7(ast)) {
if (ast->tx_chip_types & AST_TX_ASTDP_BIT)
if (ast->tx_chip == AST_TX_ASTDP)
ast_dp_launch(ast);
} else if (ast->config_mode == ast_use_p2a) {
if (IS_AST_GEN6(ast))
ast_post_chip_2500(dev);
ast_post_chip_2500(ast);
else if (IS_AST_GEN5(ast) || IS_AST_GEN4(ast))
ast_post_chip_2300(dev);
ast_post_chip_2300(ast);
else
ast_init_dram_reg(dev);
ast_init_dram_reg(ast);
ast_init_3rdtx(dev);
ast_init_3rdtx(ast);
} else {
if (ast->tx_chip_types & AST_TX_SIL164_BIT)
if (ast->tx_chip == AST_TX_SIL164)
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x80); /* Enable DVO */
}
}
@ -1569,9 +1564,8 @@ ddr2_init_start:
}
static void ast_post_chip_2300(struct drm_device *dev)
static void ast_post_chip_2300(struct ast_device *ast)
{
struct ast_device *ast = to_ast_device(dev);
struct ast2300_dram_param param;
u32 temp;
u8 reg;
@ -2038,9 +2032,9 @@ void ast_patch_ahb_2500(void __iomem *regs)
__ast_moutdwm(regs, 0x1e6e207c, 0x08000000); /* clear fast reset */
}
void ast_post_chip_2500(struct drm_device *dev)
void ast_post_chip_2500(struct ast_device *ast)
{
struct ast_device *ast = to_ast_device(dev);
struct drm_device *dev = &ast->base;
u32 temp;
u8 reg;

View File

@ -37,28 +37,29 @@
#define AST_IO_VGACRCB_HWC_16BPP BIT(0) /* set: ARGB4444, cleared: 2bpp palette */
#define AST_IO_VGACRCB_HWC_ENABLED BIT(1)
#define AST_IO_VGACRD1_MCU_FW_EXECUTING BIT(5)
#define AST_IO_VGACRD1_MCU_FW_EXECUTING BIT(5)
/* Display Transmitter Type */
#define AST_IO_VGACRD1_TX_TYPE_MASK GENMASK(3, 1)
#define AST_IO_VGACRD1_NO_TX 0x00
#define AST_IO_VGACRD1_TX_ITE66121_VBIOS 0x02
#define AST_IO_VGACRD1_TX_SIL164_VBIOS 0x04
#define AST_IO_VGACRD1_TX_CH7003_VBIOS 0x06
#define AST_IO_VGACRD1_TX_DP501_VBIOS 0x08
#define AST_IO_VGACRD1_TX_ANX9807_VBIOS 0x0a
#define AST_IO_VGACRD1_TX_FW_EMBEDDED_FW 0x0c /* special case of DP501 */
#define AST_IO_VGACRD1_TX_ASTDP 0x0e
#define AST_IO_VGACRD7_EDID_VALID_FLAG BIT(0)
#define AST_IO_VGACRDC_LINK_SUCCESS BIT(0)
#define AST_IO_VGACRDF_HPD BIT(0)
#define AST_IO_VGACRDF_DP_VIDEO_ENABLE BIT(4) /* mirrors AST_IO_VGACRE3_DP_VIDEO_ENABLE */
#define AST_IO_VGACRE3_DP_VIDEO_ENABLE BIT(0)
#define AST_IO_VGACRE3_DP_PHY_SLEEP BIT(4)
#define AST_IO_VGACRE5_EDID_READ_DONE BIT(0)
#define AST_IO_VGAIR1_R (0x5A)
#define AST_IO_VGAIR1_VREFRESH BIT(3)
/*
* Display Transmitter Type
*/
#define TX_TYPE_MASK GENMASK(3, 1)
#define NO_TX (0 << 1)
#define ITE66121_VBIOS_TX (1 << 1)
#define SI164_VBIOS_TX (2 << 1)
#define CH7003_VBIOS_TX (3 << 1)
#define DP501_VBIOS_TX (4 << 1)
#define ANX9807_VBIOS_TX (5 << 1)
#define TX_FW_EMBEDDED_FW_TX (6 << 1)
#define ASTDP_DPMCU_TX (7 << 1)
#define AST_VRAM_INIT_STATUS_MASK GENMASK(7, 6)
//#define AST_VRAM_INIT_BY_BMC BIT(7)
@ -68,18 +69,6 @@
* AST DisplayPort
*/
/* Define for Soc scratched reg used on ASTDP */
#define AST_DP_PHY_SLEEP BIT(4)
#define AST_DP_VIDEO_ENABLE BIT(0)
/*
* CRDF[b4]: Mirror of AST_DP_VIDEO_ENABLE
* Precondition: A. ~AST_DP_PHY_SLEEP &&
* B. DP_HPD &&
* C. DP_LINK_SUCCESS
*/
#define ASTDP_MIRROR_VIDEO_ENABLE BIT(4)
/*
* ASTDP setmode registers:
* CRE0[7:0]: MISC0 ((0x00: 18-bpp) or (0x20: 24-bpp)

View File

@ -71,52 +71,49 @@ static const struct drm_connector_funcs ast_sil164_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int ast_sil164_connector_init(struct drm_device *dev, struct drm_connector *connector)
{
struct ast_device *ast = to_ast_device(dev);
struct i2c_adapter *ddc;
int ret;
ddc = ast_ddc_create(ast);
if (IS_ERR(ddc)) {
ret = PTR_ERR(ddc);
drm_err(dev, "failed to add DDC bus for connector; ret=%d\n", ret);
return ret;
}
ret = drm_connector_init_with_ddc(dev, connector, &ast_sil164_connector_funcs,
DRM_MODE_CONNECTOR_DVII, ddc);
if (ret)
return ret;
drm_connector_helper_add(connector, &ast_sil164_connector_helper_funcs);
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
return 0;
}
/*
* Output
*/
int ast_sil164_output_init(struct ast_device *ast)
{
struct drm_device *dev = &ast->base;
struct drm_crtc *crtc = &ast->crtc;
struct drm_encoder *encoder = &ast->output.sil164.encoder;
struct ast_connector *ast_connector = &ast->output.sil164.connector;
struct drm_connector *connector = &ast_connector->base;
struct i2c_adapter *ddc;
struct drm_encoder *encoder;
struct ast_connector *ast_connector;
struct drm_connector *connector;
int ret;
/* DDC */
ddc = ast_ddc_create(ast);
if (IS_ERR(ddc))
return PTR_ERR(ddc);
/* encoder */
encoder = &ast->output.sil164.encoder;
ret = drm_encoder_init(dev, encoder, &ast_sil164_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL);
if (ret)
return ret;
encoder->possible_crtcs = drm_crtc_mask(crtc);
ret = ast_sil164_connector_init(dev, connector);
/* connector */
ast_connector = &ast->output.sil164.connector;
connector = &ast_connector->base;
ret = drm_connector_init_with_ddc(dev, connector, &ast_sil164_connector_funcs,
DRM_MODE_CONNECTOR_DVII, ddc);
if (ret)
return ret;
drm_connector_helper_add(connector, &ast_sil164_connector_helper_funcs);
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
ast_connector->physical_status = connector->status;
ret = drm_connector_attach_encoder(connector, encoder);

View File

@ -71,52 +71,49 @@ static const struct drm_connector_funcs ast_vga_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int ast_vga_connector_init(struct drm_device *dev, struct drm_connector *connector)
{
struct ast_device *ast = to_ast_device(dev);
struct i2c_adapter *ddc;
int ret;
ddc = ast_ddc_create(ast);
if (IS_ERR(ddc)) {
ret = PTR_ERR(ddc);
drm_err(dev, "failed to add DDC bus for connector; ret=%d\n", ret);
return ret;
}
ret = drm_connector_init_with_ddc(dev, connector, &ast_vga_connector_funcs,
DRM_MODE_CONNECTOR_VGA, ddc);
if (ret)
return ret;
drm_connector_helper_add(connector, &ast_vga_connector_helper_funcs);
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
return 0;
}
/*
* Output
*/
int ast_vga_output_init(struct ast_device *ast)
{
struct drm_device *dev = &ast->base;
struct drm_crtc *crtc = &ast->crtc;
struct drm_encoder *encoder = &ast->output.vga.encoder;
struct ast_connector *ast_connector = &ast->output.vga.connector;
struct drm_connector *connector = &ast_connector->base;
struct i2c_adapter *ddc;
struct drm_encoder *encoder;
struct ast_connector *ast_connector;
struct drm_connector *connector;
int ret;
/* DDC */
ddc = ast_ddc_create(ast);
if (IS_ERR(ddc))
return PTR_ERR(ddc);
/* encoder */
encoder = &ast->output.vga.encoder;
ret = drm_encoder_init(dev, encoder, &ast_vga_encoder_funcs,
DRM_MODE_ENCODER_DAC, NULL);
if (ret)
return ret;
encoder->possible_crtcs = drm_crtc_mask(crtc);
ret = ast_vga_connector_init(dev, connector);
/* connector */
ast_connector = &ast->output.vga.connector;
connector = &ast_connector->base;
ret = drm_connector_init_with_ddc(dev, connector, &ast_vga_connector_funcs,
DRM_MODE_CONNECTOR_VGA, ddc);
if (ret)
return ret;
drm_connector_helper_add(connector, &ast_vga_connector_helper_funcs);
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
ast_connector->physical_status = connector->status;
ret = drm_connector_attach_encoder(connector, encoder);

View File

@ -368,6 +368,13 @@ config DRM_TI_DLPC3433
It supports up to 720p resolution with 60 and 120 Hz refresh
rates.
config DRM_TI_TDP158
tristate "TI TDP158 HDMI/TMDS bridge"
depends on OF
select DRM_PANEL_BRIDGE
help
Texas Instruments TDP158 HDMI/TMDS Bridge driver
config DRM_TI_TFP410
tristate "TI TFP410 DVI/HDMI bridge"
depends on OF

View File

@ -32,6 +32,7 @@ obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
obj-$(CONFIG_DRM_TI_DLPC3433) += ti-dlpc3433.o
obj-$(CONFIG_DRM_TI_SN65DSI83) += ti-sn65dsi83.o
obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o
obj-$(CONFIG_DRM_TI_TDP158) += ti-tdp158.o
obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o
obj-$(CONFIG_DRM_TI_TPD12S015) += ti-tpd12s015.o
obj-$(CONFIG_DRM_NWL_MIPI_DSI) += nwl-dsi.o

View File

@ -3,6 +3,16 @@ if ARCH_MXC || COMPILE_TEST
config DRM_IMX_LDB_HELPER
tristate
config DRM_IMX_LEGACY_BRIDGE
tristate
depends on DRM_IMX
help
This is a DRM bridge implementation for the DRM i.MX IPUv3 driver,
that uses of_get_drm_display_mode to acquire display mode.
Newer designs should not use this bridge and should use proper panel
driver instead.
config DRM_IMX8MP_DW_HDMI_BRIDGE
tristate "Freescale i.MX8MP HDMI-TX bridge support"
depends on OF

View File

@ -1,4 +1,5 @@
obj-$(CONFIG_DRM_IMX_LDB_HELPER) += imx-ldb-helper.o
obj-$(CONFIG_DRM_IMX_LEGACY_BRIDGE) += imx-legacy-bridge.o
obj-$(CONFIG_DRM_IMX8MP_DW_HDMI_BRIDGE) += imx8mp-hdmi-tx.o
obj-$(CONFIG_DRM_IMX8MP_HDMI_PVI) += imx8mp-hdmi-pvi.o
obj-$(CONFIG_DRM_IMX8QM_LDB) += imx8qm-ldb.o

View File

@ -0,0 +1,87 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Freescale i.MX drm driver
*
* bridge driver for legacy DT bindings, utilizing display-timings node
*/
#include <drm/drm_bridge.h>
#include <drm/drm_modes.h>
#include <drm/drm_probe_helper.h>
#include <drm/bridge/imx.h>
#include <video/of_display_timing.h>
#include <video/of_videomode.h>
struct imx_legacy_bridge {
struct drm_bridge base;
struct drm_display_mode mode;
u32 bus_flags;
};
#define to_imx_legacy_bridge(bridge) container_of(bridge, struct imx_legacy_bridge, base)
static int imx_legacy_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
return -EINVAL;
return 0;
}
static int imx_legacy_bridge_get_modes(struct drm_bridge *bridge,
struct drm_connector *connector)
{
struct imx_legacy_bridge *imx_bridge = to_imx_legacy_bridge(bridge);
int ret;
ret = drm_connector_helper_get_modes_fixed(connector, &imx_bridge->mode);
if (ret)
return ret;
connector->display_info.bus_flags = imx_bridge->bus_flags;
return 0;
}
struct drm_bridge_funcs imx_legacy_bridge_funcs = {
.attach = imx_legacy_bridge_attach,
.get_modes = imx_legacy_bridge_get_modes,
};
struct drm_bridge *devm_imx_drm_legacy_bridge(struct device *dev,
struct device_node *np,
int type)
{
struct imx_legacy_bridge *imx_bridge;
int ret;
imx_bridge = devm_kzalloc(dev, sizeof(*imx_bridge), GFP_KERNEL);
if (!imx_bridge)
return ERR_PTR(-ENOMEM);
ret = of_get_drm_display_mode(np,
&imx_bridge->mode,
&imx_bridge->bus_flags,
OF_USE_NATIVE_MODE);
if (ret)
return ERR_PTR(ret);
imx_bridge->mode.type |= DRM_MODE_TYPE_DRIVER;
imx_bridge->base.funcs = &imx_legacy_bridge_funcs;
imx_bridge->base.of_node = np;
imx_bridge->base.ops = DRM_BRIDGE_OP_MODES;
imx_bridge->base.type = type;
ret = devm_drm_bridge_add(dev, &imx_bridge->base);
if (ret)
return ERR_PTR(ret);
return &imx_bridge->base;
}
EXPORT_SYMBOL_GPL(devm_imx_drm_legacy_bridge);
MODULE_LICENSE("GPL");

View File

@ -23,6 +23,7 @@ imx8mp_hdmi_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
const struct drm_display_mode *mode)
{
struct imx8mp_hdmi *hdmi = (struct imx8mp_hdmi *)data;
long round_rate;
if (mode->clock < 13500)
return MODE_CLOCK_LOW;
@ -30,8 +31,14 @@ imx8mp_hdmi_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
if (mode->clock > 297000)
return MODE_CLOCK_HIGH;
if (clk_round_rate(hdmi->pixclk, mode->clock * 1000) !=
mode->clock * 1000)
round_rate = clk_round_rate(hdmi->pixclk, mode->clock * 1000);
/* imx8mp's pixel clock generator (fsl-samsung-hdmi) cannot generate
* all possible frequencies, so allow some tolerance to support more
* modes.
* Allow 0.5% difference allowed in various standards (VESA, CEA861)
* 0.5% = 5/1000 tolerance (mode->clock is 1/1000)
*/
if (abs(round_rate - mode->clock * 1000) > mode->clock * 5)
return MODE_CLOCK_RANGE;
/* We don't support double-clocked and Interlaced modes */
@ -111,12 +118,12 @@ static void imx8mp_dw_hdmi_remove(struct platform_device *pdev)
dw_hdmi_remove(hdmi->dw_hdmi);
}
static int __maybe_unused imx8mp_dw_hdmi_pm_suspend(struct device *dev)
static int imx8mp_dw_hdmi_pm_suspend(struct device *dev)
{
return 0;
}
static int __maybe_unused imx8mp_dw_hdmi_pm_resume(struct device *dev)
static int imx8mp_dw_hdmi_pm_resume(struct device *dev)
{
struct imx8mp_hdmi *hdmi = dev_get_drvdata(dev);
@ -126,8 +133,7 @@ static int __maybe_unused imx8mp_dw_hdmi_pm_resume(struct device *dev)
}
static const struct dev_pm_ops imx8mp_dw_hdmi_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(imx8mp_dw_hdmi_pm_suspend,
imx8mp_dw_hdmi_pm_resume)
SYSTEM_SLEEP_PM_OPS(imx8mp_dw_hdmi_pm_suspend, imx8mp_dw_hdmi_pm_resume)
};
static const struct of_device_id imx8mp_dw_hdmi_of_table[] = {
@ -142,7 +148,7 @@ static struct platform_driver imx8mp_dw_hdmi_platform_driver = {
.driver = {
.name = "imx8mp-dw-hdmi-tx",
.of_match_table = imx8mp_dw_hdmi_of_table,
.pm = &imx8mp_dw_hdmi_pm_ops,
.pm = pm_ptr(&imx8mp_dw_hdmi_pm_ops),
},
};

View File

@ -542,12 +542,12 @@ static void imx8qm_ldb_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
static int __maybe_unused imx8qm_ldb_runtime_suspend(struct device *dev)
static int imx8qm_ldb_runtime_suspend(struct device *dev)
{
return 0;
}
static int __maybe_unused imx8qm_ldb_runtime_resume(struct device *dev)
static int imx8qm_ldb_runtime_resume(struct device *dev)
{
struct imx8qm_ldb *imx8qm_ldb = dev_get_drvdata(dev);
struct ldb *ldb = &imx8qm_ldb->base;
@ -559,8 +559,7 @@ static int __maybe_unused imx8qm_ldb_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops imx8qm_ldb_pm_ops = {
SET_RUNTIME_PM_OPS(imx8qm_ldb_runtime_suspend,
imx8qm_ldb_runtime_resume, NULL)
RUNTIME_PM_OPS(imx8qm_ldb_runtime_suspend, imx8qm_ldb_runtime_resume, NULL)
};
static const struct of_device_id imx8qm_ldb_dt_ids[] = {
@ -573,7 +572,7 @@ static struct platform_driver imx8qm_ldb_driver = {
.probe = imx8qm_ldb_probe,
.remove_new = imx8qm_ldb_remove,
.driver = {
.pm = &imx8qm_ldb_pm_ops,
.pm = pm_ptr(&imx8qm_ldb_pm_ops),
.name = DRIVER_NAME,
.of_match_table = imx8qm_ldb_dt_ids,
},

View File

@ -678,12 +678,12 @@ static void imx8qxp_ldb_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
static int __maybe_unused imx8qxp_ldb_runtime_suspend(struct device *dev)
static int imx8qxp_ldb_runtime_suspend(struct device *dev)
{
return 0;
}
static int __maybe_unused imx8qxp_ldb_runtime_resume(struct device *dev)
static int imx8qxp_ldb_runtime_resume(struct device *dev)
{
struct imx8qxp_ldb *imx8qxp_ldb = dev_get_drvdata(dev);
struct ldb *ldb = &imx8qxp_ldb->base;
@ -695,8 +695,7 @@ static int __maybe_unused imx8qxp_ldb_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops imx8qxp_ldb_pm_ops = {
SET_RUNTIME_PM_OPS(imx8qxp_ldb_runtime_suspend,
imx8qxp_ldb_runtime_resume, NULL)
RUNTIME_PM_OPS(imx8qxp_ldb_runtime_suspend, imx8qxp_ldb_runtime_resume, NULL)
};
static const struct of_device_id imx8qxp_ldb_dt_ids[] = {
@ -709,7 +708,7 @@ static struct platform_driver imx8qxp_ldb_driver = {
.probe = imx8qxp_ldb_probe,
.remove_new = imx8qxp_ldb_remove,
.driver = {
.pm = &imx8qxp_ldb_pm_ops,
.pm = pm_ptr(&imx8qxp_ldb_pm_ops),
.name = DRIVER_NAME,
.of_match_table = imx8qxp_ldb_dt_ids,
},

View File

@ -371,7 +371,7 @@ static void imx8qxp_pc_bridge_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
static int __maybe_unused imx8qxp_pc_runtime_suspend(struct device *dev)
static int imx8qxp_pc_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct imx8qxp_pc *pc = platform_get_drvdata(pdev);
@ -393,7 +393,7 @@ static int __maybe_unused imx8qxp_pc_runtime_suspend(struct device *dev)
return ret;
}
static int __maybe_unused imx8qxp_pc_runtime_resume(struct device *dev)
static int imx8qxp_pc_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct imx8qxp_pc *pc = platform_get_drvdata(pdev);
@ -415,8 +415,7 @@ static int __maybe_unused imx8qxp_pc_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops imx8qxp_pc_pm_ops = {
SET_RUNTIME_PM_OPS(imx8qxp_pc_runtime_suspend,
imx8qxp_pc_runtime_resume, NULL)
RUNTIME_PM_OPS(imx8qxp_pc_runtime_suspend, imx8qxp_pc_runtime_resume, NULL)
};
static const struct of_device_id imx8qxp_pc_dt_ids[] = {
@ -430,7 +429,7 @@ static struct platform_driver imx8qxp_pc_bridge_driver = {
.probe = imx8qxp_pc_bridge_probe,
.remove_new = imx8qxp_pc_bridge_remove,
.driver = {
.pm = &imx8qxp_pc_pm_ops,
.pm = pm_ptr(&imx8qxp_pc_pm_ops),
.name = DRIVER_NAME,
.of_match_table = imx8qxp_pc_dt_ids,
},

View File

@ -2043,7 +2043,7 @@ void samsung_dsim_remove(struct platform_device *pdev)
}
EXPORT_SYMBOL_GPL(samsung_dsim_remove);
static int __maybe_unused samsung_dsim_suspend(struct device *dev)
static int samsung_dsim_suspend(struct device *dev)
{
struct samsung_dsim *dsi = dev_get_drvdata(dev);
const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
@ -2073,7 +2073,7 @@ static int __maybe_unused samsung_dsim_suspend(struct device *dev)
return 0;
}
static int __maybe_unused samsung_dsim_resume(struct device *dev)
static int samsung_dsim_resume(struct device *dev)
{
struct samsung_dsim *dsi = dev_get_drvdata(dev);
const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
@ -2108,7 +2108,7 @@ err_clk:
}
const struct dev_pm_ops samsung_dsim_pm_ops = {
SET_RUNTIME_PM_OPS(samsung_dsim_suspend, samsung_dsim_resume, NULL)
RUNTIME_PM_OPS(samsung_dsim_suspend, samsung_dsim_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
@ -2142,7 +2142,7 @@ static struct platform_driver samsung_dsim_driver = {
.remove_new = samsung_dsim_remove,
.driver = {
.name = "samsung-dsim",
.pm = &samsung_dsim_pm_ops,
.pm = pm_ptr(&samsung_dsim_pm_ops),
.of_match_table = samsung_dsim_of_match,
},
};

View File

@ -312,7 +312,7 @@ static void dw_hdmi_cec_remove(struct platform_device *pdev)
cec_unregister_adapter(cec->adap);
}
static int __maybe_unused dw_hdmi_cec_resume(struct device *dev)
static int dw_hdmi_cec_resume(struct device *dev)
{
struct dw_hdmi_cec *cec = dev_get_drvdata(dev);
@ -328,7 +328,7 @@ static int __maybe_unused dw_hdmi_cec_resume(struct device *dev)
return 0;
}
static int __maybe_unused dw_hdmi_cec_suspend(struct device *dev)
static int dw_hdmi_cec_suspend(struct device *dev)
{
struct dw_hdmi_cec *cec = dev_get_drvdata(dev);
@ -341,7 +341,7 @@ static int __maybe_unused dw_hdmi_cec_suspend(struct device *dev)
}
static const struct dev_pm_ops dw_hdmi_cec_pm = {
SET_SYSTEM_SLEEP_PM_OPS(dw_hdmi_cec_suspend, dw_hdmi_cec_resume)
SYSTEM_SLEEP_PM_OPS(dw_hdmi_cec_suspend, dw_hdmi_cec_resume)
};
static struct platform_driver dw_hdmi_cec_driver = {
@ -349,7 +349,7 @@ static struct platform_driver dw_hdmi_cec_driver = {
.remove_new = dw_hdmi_cec_remove,
.driver = {
.name = "dw-hdmi-cec",
.pm = &dw_hdmi_cec_pm,
.pm = pm_ptr(&dw_hdmi_cec_pm),
},
};
module_platform_driver(dw_hdmi_cec_driver);

View File

@ -2169,19 +2169,31 @@ static const struct regmap_access_table tc_precious_table = {
.n_yes_ranges = ARRAY_SIZE(tc_precious_ranges),
};
static const struct regmap_range tc_non_writeable_ranges[] = {
regmap_reg_range(PPI_BUSYPPI, PPI_BUSYPPI),
regmap_reg_range(DSI_BUSYDSI, DSI_BUSYDSI),
regmap_reg_range(DSI_LANESTATUS0, DSI_INTSTATUS),
regmap_reg_range(TC_IDREG, SYSSTAT),
regmap_reg_range(GPIOI, GPIOI),
regmap_reg_range(DP0_LTSTAT, DP0_SNKLTCHGREQ),
};
static const struct regmap_access_table tc_writeable_table = {
.no_ranges = tc_non_writeable_ranges,
.n_no_ranges = ARRAY_SIZE(tc_non_writeable_ranges),
};
static bool tc_writeable_reg(struct device *dev, unsigned int reg)
{
/* RO reg */
switch (reg) {
case PPI_BUSYPPI:
case DSI_BUSYDSI:
case DSI_LANESTATUS0:
case DSI_LANESTATUS1:
case DSI_INTSTATUS:
case TC_IDREG:
case SYSBOOT:
case SYSSTAT:
case GPIOI:
case DP0_LTSTAT:
case DP0_SNKLTCHGREQ:
return false;
}
/* WO reg */
switch (reg) {
case DSI_STARTDSI:
case DSI_INTCLR:
return true;
}
return tc_readable_reg(dev, reg);
}
static const struct regmap_config tc_regmap_config = {
.name = "tc358767",
@ -2191,9 +2203,9 @@ static const struct regmap_config tc_regmap_config = {
.max_register = PLL_DBG,
.cache_type = REGCACHE_MAPLE,
.readable_reg = tc_readable_reg,
.writeable_reg = tc_writeable_reg,
.volatile_table = &tc_volatile_table,
.precious_table = &tc_precious_table,
.wr_table = &tc_writeable_table,
.reg_format_endian = REGMAP_ENDIAN_BIG,
.val_format_endian = REGMAP_ENDIAN_LITTLE,
};
@ -2229,11 +2241,11 @@ static irqreturn_t tc_irq_handler(int irq, void *arg)
bool h = val & INT_GPIO_H(tc->hpd_pin);
bool lc = val & INT_GPIO_LC(tc->hpd_pin);
dev_dbg(tc->dev, "GPIO%d: %s %s\n", tc->hpd_pin,
h ? "H" : "", lc ? "LC" : "");
if (h || lc)
if (h || lc) {
dev_dbg(tc->dev, "GPIO%d: %s %s\n", tc->hpd_pin,
h ? "H" : "", lc ? "LC" : "");
drm_kms_helper_hotplug_event(tc->bridge.dev);
}
}
regmap_write(tc->regmap, INTSTS_G, val);
@ -2298,7 +2310,8 @@ static int tc_probe_dpi_bridge_endpoint(struct tc_data *tc)
/* port@1 is the DPI input/output port */
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, &bridge);
if (ret && ret != -ENODEV)
return ret;
return dev_err_probe(dev, ret,
"Could not find DPI panel or bridge\n");
if (panel) {
bridge = devm_drm_panel_bridge_add(dev, panel);
@ -2326,7 +2339,8 @@ static int tc_probe_edp_bridge_endpoint(struct tc_data *tc)
/* port@2 is the output port */
ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, NULL);
if (ret && ret != -ENODEV)
return ret;
return dev_err_probe(dev, ret,
"Could not find DSI panel or bridge\n");
if (panel) {
struct drm_bridge *panel_bridge;
@ -2550,7 +2564,7 @@ static int tc_probe(struct i2c_client *client)
ret = tc_mipi_dsi_host_attach(tc);
if (ret) {
drm_bridge_remove(&tc->bridge);
return ret;
return dev_err_probe(dev, ret, "Failed to attach DSI host\n");
}
}

View File

@ -1635,8 +1635,8 @@ static void ti_sn_pwm_unregister(void)
}
#else
static inline int ti_sn_pwm_pin_request(struct ti_sn65dsi86 *pdata) { return 0; }
static inline void ti_sn_pwm_pin_release(struct ti_sn65dsi86 *pdata) {}
static inline int __maybe_unused ti_sn_pwm_pin_request(struct ti_sn65dsi86 *pdata) { return 0; }
static inline void __maybe_unused ti_sn_pwm_pin_release(struct ti_sn65dsi86 *pdata) {}
static inline int ti_sn_pwm_register(void) { return 0; }
static inline void ti_sn_pwm_unregister(void) {}

View File

@ -0,0 +1,111 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2024 Freebox SAS
*/
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
struct tdp158 {
struct drm_bridge bridge;
struct drm_bridge *next;
struct gpio_desc *enable; // Operation Enable - pin 36
struct regulator *vcc; // 3.3V
struct regulator *vdd; // 1.1V
struct device *dev;
};
static void tdp158_enable(struct drm_bridge *bridge, struct drm_bridge_state *prev)
{
int err;
struct tdp158 *tdp158 = bridge->driver_private;
err = regulator_enable(tdp158->vcc);
if (err)
dev_err(tdp158->dev, "failed to enable vcc: %d", err);
err = regulator_enable(tdp158->vdd);
if (err)
dev_err(tdp158->dev, "failed to enable vdd: %d", err);
gpiod_set_value_cansleep(tdp158->enable, 1);
}
static void tdp158_disable(struct drm_bridge *bridge, struct drm_bridge_state *prev)
{
struct tdp158 *tdp158 = bridge->driver_private;
gpiod_set_value_cansleep(tdp158->enable, 0);
regulator_disable(tdp158->vdd);
regulator_disable(tdp158->vcc);
}
static int tdp158_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags)
{
struct tdp158 *tdp158 = bridge->driver_private;
return drm_bridge_attach(bridge->encoder, tdp158->next, bridge, flags);
}
static const struct drm_bridge_funcs tdp158_bridge_funcs = {
.attach = tdp158_attach,
.atomic_enable = tdp158_enable,
.atomic_disable = tdp158_disable,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
};
static int tdp158_probe(struct i2c_client *client)
{
struct tdp158 *tdp158;
struct device *dev = &client->dev;
tdp158 = devm_kzalloc(dev, sizeof(*tdp158), GFP_KERNEL);
if (!tdp158)
return -ENOMEM;
tdp158->next = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
if (IS_ERR(tdp158->next))
return dev_err_probe(dev, PTR_ERR(tdp158->next), "missing bridge");
tdp158->vcc = devm_regulator_get(dev, "vcc");
if (IS_ERR(tdp158->vcc))
return dev_err_probe(dev, PTR_ERR(tdp158->vcc), "vcc");
tdp158->vdd = devm_regulator_get(dev, "vdd");
if (IS_ERR(tdp158->vdd))
return dev_err_probe(dev, PTR_ERR(tdp158->vdd), "vdd");
tdp158->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(tdp158->enable))
return dev_err_probe(dev, PTR_ERR(tdp158->enable), "enable");
tdp158->bridge.of_node = dev->of_node;
tdp158->bridge.funcs = &tdp158_bridge_funcs;
tdp158->bridge.driver_private = tdp158;
tdp158->dev = dev;
return devm_drm_bridge_add(dev, &tdp158->bridge);
}
static const struct of_device_id tdp158_match_table[] = {
{ .compatible = "ti,tdp158" },
{ }
};
MODULE_DEVICE_TABLE(of, tdp158_match_table);
static struct i2c_driver tdp158_driver = {
.probe = tdp158_probe,
.driver = {
.name = "tdp158",
.of_match_table = tdp158_match_table,
},
};
module_i2c_driver(tdp158_driver);
MODULE_DESCRIPTION("TI TDP158 driver");
MODULE_LICENSE("GPL");

View File

@ -64,6 +64,12 @@ config DRM_DISPLAY_DP_TUNNEL_STATE_DEBUG
If in doubt, say "N".
config DRM_DISPLAY_DSC_HELPER
bool
depends on DRM_DISPLAY_HELPER
help
DRM display helpers for VESA DSC (used by DSI and DisplayPort).
config DRM_DISPLAY_HDCP_HELPER
bool
help

View File

@ -8,10 +8,11 @@ drm_display_helper-$(CONFIG_DRM_BRIDGE_CONNECTOR) += \
drm_display_helper-$(CONFIG_DRM_DISPLAY_DP_HELPER) += \
drm_dp_dual_mode_helper.o \
drm_dp_helper.o \
drm_dp_mst_topology.o \
drm_dsc_helper.o
drm_dp_mst_topology.o
drm_display_helper-$(CONFIG_DRM_DISPLAY_DP_TUNNEL) += \
drm_dp_tunnel.o
drm_display_helper-$(CONFIG_DRM_DISPLAY_DSC_HELPER) += \
drm_dsc_helper.o
drm_display_helper-$(CONFIG_DRM_DISPLAY_HDCP_HELPER) += drm_hdcp_helper.o
drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_HELPER) += \
drm_hdmi_helper.o \

View File

@ -3015,7 +3015,7 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
bool stall)
{
int i, ret;
unsigned long flags;
unsigned long flags = 0;
struct drm_connector *connector;
struct drm_connector_state *old_conn_state, *new_conn_state;
struct drm_crtc *crtc;

View File

@ -99,6 +99,7 @@ int drm_framebuffer_check_src_coords(uint32_t src_x, uint32_t src_y,
return 0;
}
EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_framebuffer_check_src_coords);
/**
* drm_mode_addfb - add an FB to the graphics configuration
@ -838,6 +839,7 @@ void drm_framebuffer_free(struct kref *kref)
fb->funcs->destroy(fb);
}
EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_framebuffer_free);
/**
* drm_framebuffer_init - initialize a framebuffer

View File

@ -16,7 +16,6 @@
#include <drm/drm_mode.h>
#include <drm/drm_plane.h>
#include <drm/drm_prime.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/ttm/ttm_range_manager.h>
#include <drm/ttm/ttm_tt.h>
@ -686,50 +685,6 @@ drm_gem_vram_plane_helper_cleanup_fb(struct drm_plane *plane,
}
EXPORT_SYMBOL(drm_gem_vram_plane_helper_cleanup_fb);
/*
* Helpers for struct drm_simple_display_pipe_funcs
*/
/**
* drm_gem_vram_simple_display_pipe_prepare_fb() - Implements &struct
* drm_simple_display_pipe_funcs.prepare_fb
* @pipe: a simple display pipe
* @new_state: the plane's new state
*
* During plane updates, this function pins the GEM VRAM
* objects of the plane's new framebuffer to VRAM. Call
* drm_gem_vram_simple_display_pipe_cleanup_fb() to unpin them.
*
* Returns:
* 0 on success, or
* a negative errno code otherwise.
*/
int drm_gem_vram_simple_display_pipe_prepare_fb(
struct drm_simple_display_pipe *pipe,
struct drm_plane_state *new_state)
{
return drm_gem_vram_plane_helper_prepare_fb(&pipe->plane, new_state);
}
EXPORT_SYMBOL(drm_gem_vram_simple_display_pipe_prepare_fb);
/**
* drm_gem_vram_simple_display_pipe_cleanup_fb() - Implements &struct
* drm_simple_display_pipe_funcs.cleanup_fb
* @pipe: a simple display pipe
* @old_state: the plane's old state
*
* During plane updates, this function unpins the GEM VRAM
* objects of the plane's old framebuffer from VRAM. Complements
* drm_gem_vram_simple_display_pipe_prepare_fb().
*/
void drm_gem_vram_simple_display_pipe_cleanup_fb(
struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state)
{
drm_gem_vram_plane_helper_cleanup_fb(&pipe->plane, old_state);
}
EXPORT_SYMBOL(drm_gem_vram_simple_display_pipe_cleanup_fb);
/*
* PRIME helpers
*/

View File

@ -151,7 +151,7 @@ static void show_leaks(struct drm_mm *mm) { }
INTERVAL_TREE_DEFINE(struct drm_mm_node, rb,
u64, __subtree_last,
START, LAST, static inline, drm_mm_interval_tree)
START, LAST, static inline __maybe_unused, drm_mm_interval_tree)
struct drm_mm_node *
__drm_mm_interval_first(const struct drm_mm *mm, u64 start, u64 last)
@ -611,7 +611,7 @@ int drm_mm_insert_node_in_range(struct drm_mm * const mm,
}
EXPORT_SYMBOL(drm_mm_insert_node_in_range);
static inline bool drm_mm_node_scanned_block(const struct drm_mm_node *node)
static inline __maybe_unused bool drm_mm_node_scanned_block(const struct drm_mm_node *node)
{
return test_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags);
}

View File

@ -81,6 +81,7 @@ int drm_mode_object_add(struct drm_device *dev,
{
return __drm_mode_object_add(dev, obj, obj_type, true, NULL);
}
EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_mode_object_add);
void drm_mode_object_register(struct drm_device *dev,
struct drm_mode_object *obj)

View File

@ -72,7 +72,7 @@ static enum drm_gpu_sched_stat etnaviv_sched_timedout_job(struct drm_sched_job
drm_sched_resubmit_jobs(&gpu->sched);
drm_sched_start(&gpu->sched);
drm_sched_start(&gpu->sched, 0);
return DRM_GPU_SCHED_STAT_NOMINAL;
out_no_timeout:

View File

@ -883,27 +883,32 @@ static const struct drm_connector_funcs hdmi_connector_funcs = {
static int hdmi_get_modes(struct drm_connector *connector)
{
struct hdmi_context *hdata = connector_to_hdmi(connector);
struct edid *edid;
const struct drm_display_info *info = &connector->display_info;
const struct drm_edid *drm_edid;
int ret;
if (!hdata->ddc_adpt)
goto no_edid;
edid = drm_get_edid(connector, hdata->ddc_adpt);
if (!edid)
drm_edid = drm_edid_read_ddc(connector, hdata->ddc_adpt);
ret = drm_edid_connector_update(connector, drm_edid);
if (ret)
return 0;
cec_notifier_set_phys_addr(hdata->notifier, info->source_physical_address);
if (!drm_edid)
goto no_edid;
hdata->dvi_mode = !connector->display_info.is_hdmi;
hdata->dvi_mode = !info->is_hdmi;
DRM_DEV_DEBUG_KMS(hdata->dev, "%s : width[%d] x height[%d]\n",
(hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"),
edid->width_cm, edid->height_cm);
info->width_mm / 10, info->height_mm / 10);
drm_connector_update_edid_property(connector, edid);
cec_notifier_set_phys_addr_from_edid(hdata->notifier, edid);
ret = drm_edid_connector_add_modes(connector);
ret = drm_add_edid_modes(connector, edid);
kfree(edid);
drm_edid_free(drm_edid);
return ret;

View File

@ -11,6 +11,7 @@ config DRM_I915
select SHMEM
select TMPFS
select DRM_DISPLAY_DP_HELPER
select DRM_DISPLAY_DSC_HELPER
select DRM_DISPLAY_HDCP_HELPER
select DRM_DISPLAY_HDMI_HELPER
select DRM_DISPLAY_HELPER

View File

@ -321,7 +321,7 @@ static int pvr_kccb_reserve_slot_sync(struct pvr_device *pvr_dev)
bool reserved = false;
u32 retries = 0;
while ((jiffies - start_timestamp) < (u32)RESERVE_SLOT_TIMEOUT ||
while (time_before(jiffies, start_timestamp + RESERVE_SLOT_TIMEOUT) ||
retries < RESERVE_SLOT_MIN_RETRIES) {
reserved = pvr_kccb_try_reserve_slot(pvr_dev);
if (reserved)

View File

@ -69,26 +69,14 @@ process_static_context_state(struct pvr_device *pvr_dev, const struct pvr_stream
void *stream;
int err;
stream = kzalloc(stream_size, GFP_KERNEL);
if (!stream)
return -ENOMEM;
if (copy_from_user(stream, u64_to_user_ptr(stream_user_ptr), stream_size)) {
err = -EFAULT;
goto err_free;
}
stream = memdup_user(u64_to_user_ptr(stream_user_ptr), stream_size);
if (IS_ERR(stream))
return PTR_ERR(stream);
err = pvr_stream_process(pvr_dev, cmd_defs, stream, stream_size, dest);
if (err)
goto err_free;
kfree(stream);
return 0;
err_free:
kfree(stream);
return err;
}

View File

@ -220,7 +220,7 @@ err_drm_dev_exit:
return ret;
}
static __always_inline u64
static __always_inline __maybe_unused u64
pvr_fw_version_packed(u32 major, u32 minor)
{
return ((u64)major << 32) | minor;

View File

@ -90,20 +90,13 @@ static int pvr_fw_cmd_init(struct pvr_device *pvr_dev, struct pvr_job *job,
void *stream;
int err;
stream = kzalloc(stream_len, GFP_KERNEL);
if (!stream)
return -ENOMEM;
if (copy_from_user(stream, u64_to_user_ptr(stream_userptr), stream_len)) {
err = -EFAULT;
goto err_free_stream;
}
stream = memdup_user(u64_to_user_ptr(stream_userptr), stream_len);
if (IS_ERR(stream))
return PTR_ERR(stream);
err = pvr_job_process_stream(pvr_dev, stream_def, stream, stream_len, job);
err_free_stream:
kfree(stream);
return err;
}

View File

@ -782,7 +782,7 @@ static void pvr_queue_start(struct pvr_queue *queue)
}
}
drm_sched_start(&queue->scheduler);
drm_sched_start(&queue->scheduler, 0);
}
/**
@ -842,7 +842,7 @@ pvr_queue_timedout_job(struct drm_sched_job *s_job)
}
mutex_unlock(&pvr_dev->queues.lock);
drm_sched_start(sched);
drm_sched_start(sched, 0);
return DRM_GPU_SCHED_STAT_NOMINAL;
}

View File

@ -640,9 +640,7 @@ pvr_vm_context_lookup(struct pvr_file *pvr_file, u32 handle)
xa_lock(&pvr_file->vm_ctx_handles);
vm_ctx = xa_load(&pvr_file->vm_ctx_handles, handle);
if (vm_ctx)
kref_get(&vm_ctx->ref_count);
pvr_vm_context_get(vm_ctx);
xa_unlock(&pvr_file->vm_ctx_handles);
return vm_ctx;

View File

@ -11,8 +11,9 @@ config DRM_IMX
config DRM_IMX_PARALLEL_DISPLAY
tristate "Support for parallel displays"
select DRM_PANEL
depends on DRM_IMX
select DRM_BRIDGE
select DRM_PANEL_BRIDGE
select VIDEOMODE_HELPERS
config DRM_IMX_TVE
@ -26,9 +27,12 @@ config DRM_IMX_TVE
config DRM_IMX_LDB
tristate "Support for LVDS displays"
depends on DRM_IMX && MFD_SYSCON
depends on DRM_IMX
depends on COMMON_CLK
select DRM_PANEL
select MFD_SYSCON
select DRM_BRIDGE
select DRM_PANEL_BRIDGE
select DRM_IMX_LEGACY_BRIDGE
help
Choose this to enable the internal LVDS Display Bridge (LDB)
found on i.MX53 and i.MX6 processors.

View File

@ -34,13 +34,6 @@ module_param(legacyfb_depth, int, 0444);
DEFINE_DRM_GEM_DMA_FOPS(imx_drm_driver_fops);
void imx_drm_connector_destroy(struct drm_connector *connector)
{
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
}
EXPORT_SYMBOL_GPL(imx_drm_connector_destroy);
static int imx_drm_atomic_check(struct drm_device *dev,
struct drm_atomic_state *state)
{

View File

@ -3,14 +3,9 @@
#define _IMX_DRM_H_
struct device_node;
struct drm_crtc;
struct drm_connector;
struct drm_device;
struct drm_display_mode;
struct drm_encoder;
struct drm_framebuffer;
struct drm_plane;
struct platform_device;
struct imx_crtc_state {
struct drm_crtc_state base;
@ -24,21 +19,12 @@ static inline struct imx_crtc_state *to_imx_crtc_state(struct drm_crtc_state *s)
{
return container_of(s, struct imx_crtc_state, base);
}
int imx_drm_init_drm(struct platform_device *pdev,
int preferred_bpp);
int imx_drm_exit_drm(void);
extern struct platform_driver ipu_drm_driver;
void imx_drm_mode_config_init(struct drm_device *drm);
struct drm_gem_dma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb);
int imx_drm_encoder_parse_of(struct drm_device *drm,
struct drm_encoder *encoder, struct device_node *np);
void imx_drm_connector_destroy(struct drm_connector *connector);
int ipu_planes_assign_pre(struct drm_device *dev,
struct drm_atomic_state *state);

View File

@ -19,19 +19,16 @@
#include <linux/regmap.h>
#include <linux/videodev2.h>
#include <video/of_display_timing.h>
#include <video/of_videomode.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_edid.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_managed.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/bridge/imx.h>
#include "imx-drm.h"
@ -55,7 +52,6 @@
struct imx_ldb_channel;
struct imx_ldb_encoder {
struct drm_connector connector;
struct drm_encoder encoder;
struct imx_ldb_channel *channel;
};
@ -65,25 +61,13 @@ struct imx_ldb;
struct imx_ldb_channel {
struct imx_ldb *ldb;
/* Defines what is connected to the ldb, only one at a time */
struct drm_panel *panel;
struct drm_bridge *bridge;
struct device_node *child;
struct i2c_adapter *ddc;
int chno;
const struct drm_edid *drm_edid;
struct drm_display_mode mode;
int mode_valid;
u32 bus_format;
u32 bus_flags;
};
static inline struct imx_ldb_channel *con_to_imx_ldb_ch(struct drm_connector *c)
{
return container_of(c, struct imx_ldb_encoder, connector)->channel;
}
static inline struct imx_ldb_channel *enc_to_imx_ldb_ch(struct drm_encoder *e)
{
return container_of(e, struct imx_ldb_encoder, encoder)->channel;
@ -133,38 +117,6 @@ static void imx_ldb_ch_set_bus_format(struct imx_ldb_channel *imx_ldb_ch,
}
}
static int imx_ldb_connector_get_modes(struct drm_connector *connector)
{
struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector);
int num_modes;
num_modes = drm_panel_get_modes(imx_ldb_ch->panel, connector);
if (num_modes > 0)
return num_modes;
if (!imx_ldb_ch->drm_edid && imx_ldb_ch->ddc) {
imx_ldb_ch->drm_edid = drm_edid_read_ddc(connector,
imx_ldb_ch->ddc);
drm_edid_connector_update(connector, imx_ldb_ch->drm_edid);
}
if (imx_ldb_ch->drm_edid)
num_modes = drm_edid_connector_add_modes(connector);
if (imx_ldb_ch->mode_valid) {
struct drm_display_mode *mode;
mode = drm_mode_duplicate(connector->dev, &imx_ldb_ch->mode);
if (!mode)
return -EINVAL;
mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
num_modes++;
}
return num_modes;
}
static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno,
unsigned long serial_clk, unsigned long di_clk)
{
@ -205,8 +157,6 @@ static void imx_ldb_encoder_enable(struct drm_encoder *encoder)
return;
}
drm_panel_prepare(imx_ldb_ch->panel);
if (dual) {
clk_set_parent(ldb->clk_sel[mux], ldb->clk[0]);
clk_set_parent(ldb->clk_sel[mux], ldb->clk[1]);
@ -245,8 +195,6 @@ static void imx_ldb_encoder_enable(struct drm_encoder *encoder)
}
regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl);
drm_panel_enable(imx_ldb_ch->panel);
}
static void
@ -323,8 +271,6 @@ static void imx_ldb_encoder_disable(struct drm_encoder *encoder)
int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
int mux, ret;
drm_panel_disable(imx_ldb_ch->panel);
if (imx_ldb_ch == &ldb->channel[0] || dual)
ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;
if (imx_ldb_ch == &ldb->channel[1] || dual)
@ -358,8 +304,6 @@ static void imx_ldb_encoder_disable(struct drm_encoder *encoder)
dev_err(ldb->dev,
"unable to set di%d parent clock to original parent\n",
mux);
drm_panel_unprepare(imx_ldb_ch->panel);
}
static int imx_ldb_encoder_atomic_check(struct drm_encoder *encoder,
@ -374,11 +318,12 @@ static int imx_ldb_encoder_atomic_check(struct drm_encoder *encoder,
/* Bus format description in DT overrides connector display info. */
if (!bus_format && di->num_bus_formats) {
bus_format = di->bus_formats[0];
imx_crtc_state->bus_flags = di->bus_flags;
} else {
bus_format = imx_ldb_ch->bus_format;
imx_crtc_state->bus_flags = imx_ldb_ch->bus_flags;
}
imx_crtc_state->bus_flags = di->bus_flags;
switch (bus_format) {
case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB666_1X18;
@ -398,18 +343,6 @@ static int imx_ldb_encoder_atomic_check(struct drm_encoder *encoder,
}
static const struct drm_connector_funcs imx_ldb_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = imx_drm_connector_destroy,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static const struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = {
.get_modes = imx_ldb_connector_get_modes,
};
static const struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = {
.atomic_mode_set = imx_ldb_encoder_atomic_mode_set,
.enable = imx_ldb_encoder_enable,
@ -447,7 +380,6 @@ static int imx_ldb_register(struct drm_device *drm,
return PTR_ERR(ldb_encoder);
ldb_encoder->channel = imx_ldb_ch;
connector = &ldb_encoder->connector;
encoder = &ldb_encoder->encoder;
ret = imx_drm_encoder_parse_of(drm, encoder, imx_ldb_ch->child);
@ -466,25 +398,16 @@ static int imx_ldb_register(struct drm_device *drm,
drm_encoder_helper_add(encoder, &imx_ldb_encoder_helper_funcs);
if (imx_ldb_ch->bridge) {
ret = drm_bridge_attach(encoder, imx_ldb_ch->bridge, NULL, 0);
if (ret)
return ret;
} else {
/*
* We want to add the connector whenever there is no bridge
* that brings its own, not only when there is a panel. For
* historical reasons, the ldb driver can also work without
* a panel.
*/
drm_connector_helper_add(connector,
&imx_ldb_connector_helper_funcs);
drm_connector_init_with_ddc(drm, connector,
&imx_ldb_connector_funcs,
DRM_MODE_CONNECTOR_LVDS,
imx_ldb_ch->ddc);
drm_connector_attach_encoder(connector, encoder);
}
ret = drm_bridge_attach(encoder, imx_ldb_ch->bridge, NULL,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret)
return ret;
connector = drm_bridge_connector_init(drm, encoder);
if (IS_ERR(connector))
return PTR_ERR(connector);
drm_connector_attach_encoder(connector, encoder);
return 0;
}
@ -549,47 +472,6 @@ static const struct of_device_id imx_ldb_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, imx_ldb_dt_ids);
static int imx_ldb_panel_ddc(struct device *dev,
struct imx_ldb_channel *channel, struct device_node *child)
{
struct device_node *ddc_node;
int ret;
ddc_node = of_parse_phandle(child, "ddc-i2c-bus", 0);
if (ddc_node) {
channel->ddc = of_find_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node);
if (!channel->ddc) {
dev_warn(dev, "failed to get ddc i2c adapter\n");
return -EPROBE_DEFER;
}
}
if (!channel->ddc) {
const void *edidp;
int edid_len;
/* if no DDC available, fallback to hardcoded EDID */
dev_dbg(dev, "no ddc available\n");
edidp = of_get_property(child, "edid", &edid_len);
if (edidp) {
channel->drm_edid = drm_edid_alloc(edidp, edid_len);
if (!channel->drm_edid)
return -ENOMEM;
} else if (!channel->panel) {
/* fallback to display-timings node */
ret = of_get_drm_display_mode(child,
&channel->mode,
&channel->bus_flags,
OF_USE_NATIVE_MODE);
if (!ret)
channel->mode_valid = 1;
}
}
return 0;
}
static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
{
struct drm_device *drm = data;
@ -694,29 +576,22 @@ static int imx_ldb_probe(struct platform_device *pdev)
* The output port is port@4 with an external 4-port mux or
* port@2 with the internal 2-port mux.
*/
ret = drm_of_find_panel_or_bridge(child,
imx_ldb->lvds_mux ? 4 : 2, 0,
&channel->panel, &channel->bridge);
if (ret && ret != -ENODEV)
goto free_child;
/* panel ddc only if there is no bridge */
if (!channel->bridge) {
ret = imx_ldb_panel_ddc(dev, channel, child);
if (ret)
channel->bridge = devm_drm_of_get_bridge(dev, child,
imx_ldb->lvds_mux ? 4 : 2, 0);
if (IS_ERR(channel->bridge)) {
ret = PTR_ERR(channel->bridge);
if (ret != -ENODEV)
goto free_child;
channel->bridge = NULL;
}
bus_format = of_get_bus_format(dev, child);
if (bus_format == -EINVAL) {
/*
* If no bus format was specified in the device tree,
* we can still get it from the connected panel later.
*/
if (channel->panel && channel->panel->funcs &&
channel->panel->funcs->get_modes)
bus_format = 0;
}
/*
* If no bus format was specified in the device tree,
* we can still get it from the connected panel later.
*/
if (bus_format == -EINVAL && channel->bridge)
bus_format = 0;
if (bus_format < 0) {
dev_err(dev, "could not determine data mapping: %d\n",
bus_format);
@ -724,6 +599,20 @@ static int imx_ldb_probe(struct platform_device *pdev)
goto free_child;
}
channel->bus_format = bus_format;
/*
* legacy bridge doesn't handle bus_format, so create it after
* checking the bus_format property.
*/
if (!channel->bridge) {
channel->bridge = devm_imx_drm_legacy_bridge(dev, child,
DRM_MODE_CONNECTOR_LVDS);
if (IS_ERR(channel->bridge)) {
ret = PTR_ERR(channel->bridge);
goto free_child;
}
}
channel->child = child;
}
@ -738,16 +627,6 @@ free_child:
static void imx_ldb_remove(struct platform_device *pdev)
{
struct imx_ldb *imx_ldb = platform_get_drvdata(pdev);
int i;
for (i = 0; i < 2; i++) {
struct imx_ldb_channel *channel = &imx_ldb->channel[i];
drm_edid_free(channel->drm_edid);
i2c_put_adapter(channel->ddc);
}
component_del(&pdev->dev, &imx_ldb_ops);
}

View File

@ -305,9 +305,15 @@ static int imx_tve_atomic_check(struct drm_encoder *encoder,
return 0;
}
static void imx_tve_connector_destroy(struct drm_connector *connector)
{
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
}
static const struct drm_connector_funcs imx_tve_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = imx_drm_connector_destroy,
.destroy = imx_tve_connector_destroy,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,

View File

@ -12,21 +12,18 @@
#include <linux/platform_device.h>
#include <linux/videodev2.h>
#include <video/of_display_timing.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_edid.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_managed.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/bridge/imx.h>
#include "imx-drm.h"
struct imx_parallel_display_encoder {
struct drm_connector connector;
struct drm_encoder encoder;
struct drm_bridge bridge;
struct imx_parallel_display *pd;
@ -34,79 +31,15 @@ struct imx_parallel_display_encoder {
struct imx_parallel_display {
struct device *dev;
const struct drm_edid *drm_edid;
u32 bus_format;
u32 bus_flags;
struct drm_display_mode mode;
struct drm_panel *panel;
struct drm_bridge *next_bridge;
};
static inline struct imx_parallel_display *con_to_imxpd(struct drm_connector *c)
{
return container_of(c, struct imx_parallel_display_encoder, connector)->pd;
}
static inline struct imx_parallel_display *bridge_to_imxpd(struct drm_bridge *b)
{
return container_of(b, struct imx_parallel_display_encoder, bridge)->pd;
}
static int imx_pd_connector_get_modes(struct drm_connector *connector)
{
struct imx_parallel_display *imxpd = con_to_imxpd(connector);
struct device_node *np = imxpd->dev->of_node;
int num_modes;
num_modes = drm_panel_get_modes(imxpd->panel, connector);
if (num_modes > 0)
return num_modes;
if (imxpd->drm_edid) {
drm_edid_connector_update(connector, imxpd->drm_edid);
num_modes = drm_edid_connector_add_modes(connector);
}
if (np) {
struct drm_display_mode *mode = drm_mode_create(connector->dev);
int ret;
if (!mode)
return 0;
ret = of_get_drm_display_mode(np, &imxpd->mode,
&imxpd->bus_flags,
OF_USE_NATIVE_MODE);
if (ret) {
drm_mode_destroy(connector->dev, mode);
return 0;
}
drm_mode_copy(mode, &imxpd->mode);
mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
num_modes++;
}
return num_modes;
}
static void imx_pd_bridge_enable(struct drm_bridge *bridge)
{
struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
drm_panel_prepare(imxpd->panel);
drm_panel_enable(imxpd->panel);
}
static void imx_pd_bridge_disable(struct drm_bridge *bridge)
{
struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
drm_panel_disable(imxpd->panel);
drm_panel_unprepare(imxpd->panel);
}
static const u32 imx_pd_bus_fmts[] = {
MEDIA_BUS_FMT_RGB888_1X24,
MEDIA_BUS_FMT_BGR888_1X24,
@ -200,7 +133,6 @@ static int imx_pd_bridge_atomic_check(struct drm_bridge *bridge,
{
struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
struct drm_display_info *di = &conn_state->connector->display_info;
struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
struct drm_bridge_state *next_bridge_state = NULL;
struct drm_bridge *next_bridge;
u32 bus_flags, bus_fmt;
@ -212,10 +144,8 @@ static int imx_pd_bridge_atomic_check(struct drm_bridge *bridge,
if (next_bridge_state)
bus_flags = next_bridge_state->input_bus_cfg.flags;
else if (di->num_bus_formats)
bus_flags = di->bus_flags;
else
bus_flags = imxpd->bus_flags;
bus_flags = di->bus_flags;
bus_fmt = bridge_state->input_bus_cfg.format;
if (!imx_pd_format_supported(bus_fmt))
@ -231,21 +161,16 @@ static int imx_pd_bridge_atomic_check(struct drm_bridge *bridge,
return 0;
}
static const struct drm_connector_funcs imx_pd_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = imx_drm_connector_destroy,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int imx_pd_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
static const struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = {
.get_modes = imx_pd_connector_get_modes,
};
return drm_bridge_attach(bridge->encoder, imxpd->next_bridge, bridge, flags);
}
static const struct drm_bridge_funcs imx_pd_bridge_funcs = {
.enable = imx_pd_bridge_enable,
.disable = imx_pd_bridge_disable,
.attach = imx_pd_bridge_attach,
.atomic_reset = drm_atomic_helper_bridge_reset,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
@ -270,7 +195,6 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data)
return PTR_ERR(imxpd_encoder);
imxpd_encoder->pd = imxpd;
connector = &imxpd_encoder->connector;
encoder = &imxpd_encoder->encoder;
bridge = &imxpd_encoder->bridge;
@ -278,28 +202,14 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data)
if (ret)
return ret;
/* set the connector's dpms to OFF so that
* drm_helper_connector_dpms() won't return
* immediately since the current state is ON
* at this point.
*/
connector->dpms = DRM_MODE_DPMS_OFF;
bridge->funcs = &imx_pd_bridge_funcs;
drm_bridge_attach(encoder, bridge, NULL, 0);
drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (imxpd->next_bridge) {
ret = drm_bridge_attach(encoder, imxpd->next_bridge, bridge, 0);
if (ret < 0)
return ret;
} else {
drm_connector_helper_add(connector,
&imx_pd_connector_helper_funcs);
drm_connector_init(drm, connector, &imx_pd_connector_funcs,
DRM_MODE_CONNECTOR_DPI);
connector = drm_bridge_connector_init(drm, encoder);
if (IS_ERR(connector))
return PTR_ERR(connector);
drm_connector_attach_encoder(connector, encoder);
}
drm_connector_attach_encoder(connector, encoder);
return 0;
}
@ -312,9 +222,7 @@ static int imx_pd_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
const u8 *edidp;
struct imx_parallel_display *imxpd;
int edid_len;
int ret;
u32 bus_format = 0;
const char *fmt;
@ -324,14 +232,13 @@ static int imx_pd_probe(struct platform_device *pdev)
return -ENOMEM;
/* port@1 is the output port */
ret = drm_of_find_panel_or_bridge(np, 1, 0, &imxpd->panel,
&imxpd->next_bridge);
if (ret && ret != -ENODEV)
imxpd->next_bridge = devm_drm_of_get_bridge(dev, np, 1, 0);
if (imxpd->next_bridge == ERR_PTR(-ENODEV))
imxpd->next_bridge = devm_imx_drm_legacy_bridge(dev, np, DRM_MODE_CONNECTOR_DPI);
if (IS_ERR(imxpd->next_bridge)) {
ret = PTR_ERR(imxpd->next_bridge);
return ret;
edidp = of_get_property(np, "edid", &edid_len);
if (edidp)
imxpd->drm_edid = drm_edid_alloc(edidp, edid_len);
}
ret = of_property_read_string(np, "interface-pix-fmt", &fmt);
if (!ret) {
@ -355,11 +262,7 @@ static int imx_pd_probe(struct platform_device *pdev)
static void imx_pd_remove(struct platform_device *pdev)
{
struct imx_parallel_display *imxpd = platform_get_drvdata(pdev);
component_del(&pdev->dev, &imx_pd_ops);
drm_edid_free(imxpd->drm_edid);
}
static const struct of_device_id imx_pd_dt_ids[] = {

View File

@ -818,7 +818,7 @@ static void test_mode_send(struct kmb_dsi *kmb_dsi, u32 dphy_no,
}
}
static inline void
static inline __maybe_unused void
set_test_mode_src_osc_freq_target_low_bits(struct kmb_dsi *kmb_dsi,
u32 dphy_no,
u32 freq)
@ -830,7 +830,7 @@ static inline void
(freq & 0x7f));
}
static inline void
static inline __maybe_unused void
set_test_mode_src_osc_freq_target_hi_bits(struct kmb_dsi *kmb_dsi,
u32 dphy_no,
u32 freq)

View File

@ -463,7 +463,7 @@ static enum drm_gpu_sched_stat lima_sched_timedout_job(struct drm_sched_job *job
lima_pm_idle(ldev);
drm_sched_resubmit_jobs(&pipe->base);
drm_sched_start(&pipe->base);
drm_sched_start(&pipe->base, 0);
return DRM_GPU_SCHED_STAT_NOMINAL;
}

View File

@ -92,6 +92,7 @@ config DRM_MSM_DPU
bool "Enable DPU support in MSM DRM driver"
depends on DRM_MSM
select DRM_MSM_MDSS
select DRM_DISPLAY_DSC_HELPER
default y
help
Compile in support for the Display Processing Unit in
@ -113,6 +114,7 @@ config DRM_MSM_DSI
depends on DRM_MSM
select DRM_PANEL
select DRM_MIPI_DSI
select DRM_DISPLAY_DSC_HELPER
default y
help
Choose this option if you have a need for MIPI DSI connector

View File

@ -477,14 +477,14 @@ nouveau_connector_of_detect(struct drm_connector *connector)
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder;
struct pci_dev *pdev = to_pci_dev(dev->dev);
struct device_node *cn, *dn = pci_device_to_OF_node(pdev);
struct device_node *dn = pci_device_to_OF_node(pdev);
if (!dn ||
!((nv_encoder = find_encoder(connector, DCB_OUTPUT_TMDS)) ||
(nv_encoder = find_encoder(connector, DCB_OUTPUT_ANALOG))))
return NULL;
for_each_child_of_node(dn, cn) {
for_each_child_of_node_scoped(dn, cn) {
const char *name = of_get_property(cn, "name", NULL);
const void *edid = of_get_property(cn, "EDID", NULL);
int idx = name ? name[strlen(name) - 1] - 'A' : 0;
@ -492,7 +492,6 @@ nouveau_connector_of_detect(struct drm_connector *connector)
if (nv_encoder->dcb->i2c_index == idx && edid) {
nv_connector->edid =
kmemdup(edid, EDID_LENGTH, GFP_KERNEL);
of_node_put(cn);
return nv_encoder;
}
}

View File

@ -379,7 +379,7 @@ nouveau_sched_timedout_job(struct drm_sched_job *sched_job)
else
NV_PRINTK(warn, job->cli, "Generic job timeout.\n");
drm_sched_start(sched);
drm_sched_start(sched, 0);
return stat;
}

View File

@ -120,8 +120,8 @@ nvkm_device_tegra_probe_iommu(struct nvkm_device_tegra *tdev)
mutex_init(&tdev->iommu.mutex);
if (device_iommu_mapped(dev)) {
tdev->iommu.domain = iommu_domain_alloc(&platform_bus_type);
if (!tdev->iommu.domain)
tdev->iommu.domain = iommu_paging_domain_alloc(dev);
if (IS_ERR(tdev->iommu.domain))
goto error;
/*

View File

@ -142,7 +142,7 @@ nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp)
return -ENODEV;
}
result = min(max(result, (s64)info.min), (s64)info.max);
result = clamp(result, (s64)info.min, (s64)info.max);
if (info.link != 0xff) {
int ret = nvkm_volt_map(volt, info.link, temp);

View File

@ -139,21 +139,13 @@ static bool omapdss_device_is_connected(struct omap_dss_device *dssdev)
}
int omapdss_device_connect(struct dss_device *dss,
struct omap_dss_device *src,
struct omap_dss_device *dst)
{
dev_dbg(&dss->pdev->dev, "connect(%s, %s)\n",
src ? dev_name(src->dev) : "NULL",
dev_dbg(&dss->pdev->dev, "connect(%s)\n",
dst ? dev_name(dst->dev) : "NULL");
if (!dst) {
/*
* The destination is NULL when the source is connected to a
* bridge instead of a DSS device. Stop here, we will attach
* the bridge later when we will have a DRM encoder.
*/
return src && src->bridge ? 0 : -EINVAL;
}
if (!dst)
return -EINVAL;
if (omapdss_device_is_connected(dst))
return -EBUSY;
@ -163,19 +155,14 @@ int omapdss_device_connect(struct dss_device *dss,
return 0;
}
void omapdss_device_disconnect(struct omap_dss_device *src,
void omapdss_device_disconnect(struct dss_device *dss,
struct omap_dss_device *dst)
{
struct dss_device *dss = src ? src->dss : dst->dss;
dev_dbg(&dss->pdev->dev, "disconnect(%s, %s)\n",
src ? dev_name(src->dev) : "NULL",
dev_dbg(&dss->pdev->dev, "disconnect(%s)\n",
dst ? dev_name(dst->dev) : "NULL");
if (!dst) {
WARN_ON(!src->bridge);
if (WARN_ON(!dst))
return;
}
if (!dst->id && !omapdss_device_is_connected(dst)) {
WARN_ON(1);

View File

@ -242,9 +242,8 @@ struct omap_dss_device *omapdss_device_get(struct omap_dss_device *dssdev);
void omapdss_device_put(struct omap_dss_device *dssdev);
struct omap_dss_device *omapdss_find_device_by_node(struct device_node *node);
int omapdss_device_connect(struct dss_device *dss,
struct omap_dss_device *src,
struct omap_dss_device *dst);
void omapdss_device_disconnect(struct omap_dss_device *src,
void omapdss_device_disconnect(struct dss_device *dss,
struct omap_dss_device *dst);
int omap_dss_get_num_overlay_managers(void);

View File

@ -119,7 +119,7 @@ static u32 dmm_read_wa(struct dmm *dmm, u32 reg)
* earlier than the DMA finished writing the value to memory.
*/
rmb();
return readl(dmm->wa_dma_data);
return readl((__iomem void *)dmm->wa_dma_data);
}
static void dmm_write_wa(struct dmm *dmm, u32 val, u32 reg)
@ -127,7 +127,7 @@ static void dmm_write_wa(struct dmm *dmm, u32 val, u32 reg)
dma_addr_t src, dst;
int r;
writel(val, dmm->wa_dma_data);
writel(val, (__iomem void *)dmm->wa_dma_data);
/*
* As per i878 workaround, the DMA is used to access the DMM registers.
* Make sure that the writel is not moved by the compiler or the CPU, so
@ -411,7 +411,7 @@ static int dmm_txn_commit(struct dmm_txn *txn, bool wait)
*/
/* read back to ensure the data is in RAM */
readl(&txn->last_pat->next_pa);
readl((__iomem void *)&txn->last_pat->next_pa);
/* write to PAT_DESCR to clear out any pending transaction */
dmm_write(dmm, 0x0, reg[PAT_DESCR][engine->id]);

View File

@ -307,7 +307,7 @@ static void omap_disconnect_pipelines(struct drm_device *ddev)
for (i = 0; i < priv->num_pipes; i++) {
struct omap_drm_pipeline *pipe = &priv->pipes[i];
omapdss_device_disconnect(NULL, pipe->output);
omapdss_device_disconnect(priv->dss, pipe->output);
omapdss_device_put(pipe->output);
pipe->output = NULL;
@ -325,7 +325,7 @@ static int omap_connect_pipelines(struct drm_device *ddev)
int r;
for_each_dss_output(output) {
r = omapdss_device_connect(priv->dss, NULL, output);
r = omapdss_device_connect(priv->dss, output);
if (r == -EPROBE_DEFER) {
omapdss_device_put(output);
return r;

View File

@ -1402,8 +1402,6 @@ struct drm_gem_object *omap_gem_new_dmabuf(struct drm_device *dev, size_t size,
omap_obj = to_omap_bo(obj);
mutex_lock(&omap_obj->lock);
omap_obj->sgt = sgt;
if (omap_gem_sgt_is_contiguous(sgt, size)) {
@ -1418,21 +1416,17 @@ struct drm_gem_object *omap_gem_new_dmabuf(struct drm_device *dev, size_t size,
pages = kcalloc(npages, sizeof(*pages), GFP_KERNEL);
if (!pages) {
omap_gem_free_object(obj);
obj = ERR_PTR(-ENOMEM);
goto done;
return ERR_PTR(-ENOMEM);
}
omap_obj->pages = pages;
ret = drm_prime_sg_to_page_array(sgt, pages, npages);
if (ret) {
omap_gem_free_object(obj);
obj = ERR_PTR(-ENOMEM);
goto done;
return ERR_PTR(-ENOMEM);
}
}
done:
mutex_unlock(&omap_obj->lock);
return obj;
}

View File

@ -378,7 +378,7 @@ config DRM_PANEL_LG_SW43408
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
select DRM_DISPLAY_DP_HELPER
select DRM_DISPLAY_DSC_HELPER
select DRM_DISPLAY_HELPER
help
Say Y here if you want to enable support for LG sw43408 panel.
@ -587,7 +587,7 @@ config DRM_PANEL_RAYDIUM_RM692E5
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
select DRM_DISPLAY_DP_HELPER
select DRM_DISPLAY_DSC_HELPER
select DRM_DISPLAY_HELPER
help
Say Y here if you want to enable support for Raydium RM692E5-based
@ -946,7 +946,7 @@ config DRM_PANEL_VISIONOX_R66451
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
select DRM_DISPLAY_DP_HELPER
select DRM_DISPLAY_DSC_HELPER
select DRM_DISPLAY_HELPER
help
Say Y here if you want to enable support for Visionox

View File

@ -56,198 +56,173 @@ static void hx83112a_reset(struct hx83112a_panel *ctx)
msleep(50);
}
static int hx83112a_on(struct hx83112a_panel *ctx)
static int hx83112a_on(struct mipi_dsi_device *dsi)
{
struct mipi_dsi_device *dsi = ctx->dsi;
struct device *dev = &dsi->dev;
int ret;
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETEXTC, 0x83, 0x11, 0x2a);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETPOWER1,
0x08, 0x28, 0x28, 0x83, 0x83, 0x4c, 0x4f, 0x33);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDISP,
0x00, 0x02, 0x00, 0x90, 0x24, 0x00, 0x08, 0x19,
0xea, 0x11, 0x11, 0x00, 0x11, 0xa3);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDRV,
0x58, 0x68, 0x58, 0x68, 0x0f, 0xef, 0x0b, 0xc0,
0x0b, 0xc0, 0x0b, 0xc0, 0x00, 0xff, 0x00, 0xff,
0x00, 0x00, 0x14, 0x15, 0x00, 0x29, 0x11, 0x07,
0x12, 0x00, 0x29);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x02);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDRV,
0x00, 0x12, 0x12, 0x11, 0x88, 0x12, 0x12, 0x00,
0x53);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x00);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x03);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDGCLUT,
0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
0x40);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x02);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDGCLUT,
0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
0x40);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x01);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDGCLUT,
0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
0x40);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x00);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDGCLUT, 0x01);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETTCON,
0x70, 0x00, 0x04, 0xe0, 0x33, 0x00);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETPANEL, 0x08);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETPOWER2, 0x2b, 0x2b);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP0,
0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08,
0x08, 0x03, 0x03, 0x22, 0x18, 0x07, 0x07, 0x07,
0x07, 0x32, 0x10, 0x06, 0x00, 0x06, 0x32, 0x10,
0x07, 0x00, 0x07, 0x32, 0x19, 0x31, 0x09, 0x31,
0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x08,
0x09, 0x30, 0x00, 0x00, 0x00, 0x06, 0x0d, 0x00,
0x0f);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x01);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP0,
0x00, 0x00, 0x19, 0x10, 0x00, 0x0a, 0x00, 0x81);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x00);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP1,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0xc0, 0xc0, 0x18, 0x18, 0x19, 0x19, 0x18, 0x18,
0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x3f, 0x3f,
0x28, 0x28, 0x24, 0x24, 0x02, 0x03, 0x02, 0x03,
0x00, 0x01, 0x00, 0x01, 0x31, 0x31, 0x31, 0x31,
0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP2,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19,
0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x3f, 0x3f,
0x24, 0x24, 0x28, 0x28, 0x01, 0x00, 0x01, 0x00,
0x03, 0x02, 0x03, 0x02, 0x31, 0x31, 0x31, 0x31,
0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP3,
0xaa, 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea, 0xab, 0xaa,
0xaa, 0xaa, 0xaa, 0xea, 0xab, 0xaa, 0xaa, 0xaa);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x01);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP3,
0xaa, 0x2e, 0x28, 0x00, 0x00, 0x00, 0xaa, 0x2e,
0x28, 0x00, 0x00, 0x00, 0xaa, 0xee, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xee, 0xaa, 0xaa, 0xaa, 0xaa);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x02);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP3,
0xaa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xff,
0xff, 0xff, 0xff, 0xff);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x03);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP3,
0xaa, 0xaa, 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xff, 0xff, 0xff,
0xff, 0xff, 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x00);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETTP1,
0x0e, 0x0e, 0x1e, 0x65, 0x1c, 0x65, 0x00, 0x50,
0x20, 0x20, 0x00, 0x00, 0x02, 0x02, 0x02, 0x05,
0x14, 0x14, 0x32, 0xb9, 0x23, 0xb9, 0x08);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x01);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETTP1,
0x02, 0x00, 0xa8, 0x01, 0xa8, 0x0d, 0xa4, 0x0e);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x02);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETTP1,
0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x00);
mipi_dsi_dcs_write_seq(dsi, HX83112A_UNKNOWN1, 0xc3);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETCLOCK, 0xd1, 0xd6);
mipi_dsi_dcs_write_seq(dsi, HX83112A_UNKNOWN1, 0x3f);
mipi_dsi_dcs_write_seq(dsi, HX83112A_UNKNOWN1, 0xc6);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETPTBA, 0x37);
mipi_dsi_dcs_write_seq(dsi, HX83112A_UNKNOWN1, 0x3f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETEXTC, 0x83, 0x11, 0x2a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETPOWER1,
0x08, 0x28, 0x28, 0x83, 0x83, 0x4c, 0x4f, 0x33);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDISP,
0x00, 0x02, 0x00, 0x90, 0x24, 0x00, 0x08, 0x19,
0xea, 0x11, 0x11, 0x00, 0x11, 0xa3);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDRV,
0x58, 0x68, 0x58, 0x68, 0x0f, 0xef, 0x0b, 0xc0,
0x0b, 0xc0, 0x0b, 0xc0, 0x00, 0xff, 0x00, 0xff,
0x00, 0x00, 0x14, 0x15, 0x00, 0x29, 0x11, 0x07,
0x12, 0x00, 0x29);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDRV,
0x00, 0x12, 0x12, 0x11, 0x88, 0x12, 0x12, 0x00,
0x53);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x03);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDGCLUT,
0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
0x40);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDGCLUT,
0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
0x40);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDGCLUT,
0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
0x40);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETDGCLUT, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETTCON,
0x70, 0x00, 0x04, 0xe0, 0x33, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETPANEL, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETPOWER2, 0x2b, 0x2b);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP0,
0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08,
0x08, 0x03, 0x03, 0x22, 0x18, 0x07, 0x07, 0x07,
0x07, 0x32, 0x10, 0x06, 0x00, 0x06, 0x32, 0x10,
0x07, 0x00, 0x07, 0x32, 0x19, 0x31, 0x09, 0x31,
0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x08,
0x09, 0x30, 0x00, 0x00, 0x00, 0x06, 0x0d, 0x00,
0x0f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP0,
0x00, 0x00, 0x19, 0x10, 0x00, 0x0a, 0x00, 0x81);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP1,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0xc0, 0xc0, 0x18, 0x18, 0x19, 0x19, 0x18, 0x18,
0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x3f, 0x3f,
0x28, 0x28, 0x24, 0x24, 0x02, 0x03, 0x02, 0x03,
0x00, 0x01, 0x00, 0x01, 0x31, 0x31, 0x31, 0x31,
0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP2,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19,
0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x3f, 0x3f,
0x24, 0x24, 0x28, 0x28, 0x01, 0x00, 0x01, 0x00,
0x03, 0x02, 0x03, 0x02, 0x31, 0x31, 0x31, 0x31,
0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP3,
0xaa, 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea, 0xab, 0xaa,
0xaa, 0xaa, 0xaa, 0xea, 0xab, 0xaa, 0xaa, 0xaa);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP3,
0xaa, 0x2e, 0x28, 0x00, 0x00, 0x00, 0xaa, 0x2e,
0x28, 0x00, 0x00, 0x00, 0xaa, 0xee, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xee, 0xaa, 0xaa, 0xaa, 0xaa);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP3,
0xaa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xff,
0xff, 0xff, 0xff, 0xff);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x03);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETGIP3,
0xaa, 0xaa, 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xff, 0xff, 0xff,
0xff, 0xff, 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETTP1,
0x0e, 0x0e, 0x1e, 0x65, 0x1c, 0x65, 0x00, 0x50,
0x20, 0x20, 0x00, 0x00, 0x02, 0x02, 0x02, 0x05,
0x14, 0x14, 0x32, 0xb9, 0x23, 0xb9, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETTP1,
0x02, 0x00, 0xa8, 0x01, 0xa8, 0x0d, 0xa4, 0x0e);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETTP1,
0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETBANK, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_UNKNOWN1, 0xc3);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETCLOCK, 0xd1, 0xd6);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_UNKNOWN1, 0x3f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_UNKNOWN1, 0xc6);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_SETPTBA, 0x37);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83112A_UNKNOWN1, 0x3f);
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(150);
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 150);
ret = mipi_dsi_dcs_set_display_on(dsi);
if (ret < 0) {
dev_err(dev, "Failed to set display on: %d\n", ret);
return ret;
}
msleep(50);
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 50);
return 0;
return dsi_ctx.accum_err;
}
static int hx83112a_disable(struct drm_panel *panel)
{
struct hx83112a_panel *ctx = to_hx83112a_panel(panel);
struct mipi_dsi_device *dsi = ctx->dsi;
struct device *dev = &dsi->dev;
int ret;
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
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(20);
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 20);
mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 120);
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(120);
return 0;
return dsi_ctx.accum_err;
}
static int hx83112a_prepare(struct drm_panel *panel)
{
struct hx83112a_panel *ctx = to_hx83112a_panel(panel);
struct device *dev = &ctx->dsi->dev;
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
if (ret < 0) {
dev_err(dev, "Failed to enable regulators: %d\n", ret);
if (ret < 0)
return ret;
}
hx83112a_reset(ctx);
ret = hx83112a_on(ctx);
ret = hx83112a_on(ctx->dsi);
if (ret < 0) {
dev_err(dev, "Failed to initialize panel: %d\n", ret);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
return ret;
}
return 0;
return ret;
}
static int hx83112a_unprepare(struct drm_panel *panel)

View File

@ -13,9 +13,6 @@
* Derived from drivers/drm/gpu/panel/panel-ilitek-ili9322.c
* the reuse of DBI abstraction part referred from Linus's patch
* "drm/panel: s6e63m0: Switch to DBI abstraction for SPI"
*
* For only-dbi part, copy from David's code (drm/tiny/ili9341.c)
* Copyright 2018 David Lechner <david@lechnology.com>
*/
#include <linux/backlight.h>
@ -486,176 +483,6 @@ static const struct drm_panel_funcs ili9341_dpi_funcs = {
.get_modes = ili9341_dpi_get_modes,
};
static void ili9341_dbi_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state,
struct drm_plane_state *plane_state)
{
struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
struct mipi_dbi *dbi = &dbidev->dbi;
u8 addr_mode;
int ret, idx;
if (!drm_dev_enter(pipe->crtc.dev, &idx))
return;
ret = mipi_dbi_poweron_conditional_reset(dbidev);
if (ret < 0)
goto out_exit;
if (ret == 1)
goto out_enable;
mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
mipi_dbi_command(dbi, ILI9341_POWERB, 0x00, 0xc1, 0x30);
mipi_dbi_command(dbi, ILI9341_POWER_SEQ, 0x64, 0x03, 0x12, 0x81);
mipi_dbi_command(dbi, ILI9341_DTCA, 0x85, 0x00, 0x78);
mipi_dbi_command(dbi, ILI9341_POWERA, 0x39, 0x2c, 0x00, 0x34, 0x02);
mipi_dbi_command(dbi, ILI9341_PRC, ILI9341_DBI_PRC_NORMAL);
mipi_dbi_command(dbi, ILI9341_DTCB, 0x00, 0x00);
/* Power Control */
mipi_dbi_command(dbi, ILI9341_POWER1, ILI9341_DBI_VCOMH_4P6V);
mipi_dbi_command(dbi, ILI9341_POWER2, ILI9341_DBI_PWR_2_DEFAULT);
/* VCOM */
mipi_dbi_command(dbi, ILI9341_VCOM1, ILI9341_DBI_VCOM_1_VMH_4P25V,
ILI9341_DBI_VCOM_1_VML_1P5V);
mipi_dbi_command(dbi, ILI9341_VCOM2, ILI9341_DBI_VCOM_2_DEC_58);
/* Memory Access Control */
mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT,
MIPI_DCS_PIXEL_FMT_16BIT);
/* Frame Rate */
mipi_dbi_command(dbi, ILI9341_FRC, ILI9341_DBI_FRC_DIVA & 0x03,
ILI9341_DBI_FRC_RTNA & 0x1f);
/* Gamma */
mipi_dbi_command(dbi, ILI9341_3GAMMA_EN, 0x00);
mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, ILI9341_GAMMA_CURVE_1);
mipi_dbi_command(dbi, ILI9341_PGAMMA,
0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1,
0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00);
mipi_dbi_command(dbi, ILI9341_NGAMMA,
0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1,
0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f);
/* DDRAM */
mipi_dbi_command(dbi, ILI9341_ETMOD, ILI9341_DBI_EMS_GAS |
ILI9341_DBI_EMS_DTS |
ILI9341_DBI_EMS_GON);
/* Display */
mipi_dbi_command(dbi, ILI9341_DFC, 0x08, 0x82, 0x27, 0x00);
mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
msleep(100);
mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
msleep(100);
out_enable:
switch (dbidev->rotation) {
default:
addr_mode = ILI9341_MADCTL_MX;
break;
case 90:
addr_mode = ILI9341_MADCTL_MV;
break;
case 180:
addr_mode = ILI9341_MADCTL_MY;
break;
case 270:
addr_mode = ILI9341_MADCTL_MV | ILI9341_MADCTL_MY |
ILI9341_MADCTL_MX;
break;
}
addr_mode |= ILI9341_MADCTL_BGR;
mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
mipi_dbi_enable_flush(dbidev, crtc_state, plane_state);
drm_info(&dbidev->drm, "Initialized display serial interface\n");
out_exit:
drm_dev_exit(idx);
}
static const struct drm_simple_display_pipe_funcs ili9341_dbi_funcs = {
DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS(ili9341_dbi_enable),
};
static const struct drm_display_mode ili9341_dbi_mode = {
DRM_SIMPLE_MODE(240, 320, 37, 49),
};
DEFINE_DRM_GEM_DMA_FOPS(ili9341_dbi_fops);
static struct drm_driver ili9341_dbi_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
.fops = &ili9341_dbi_fops,
DRM_GEM_DMA_DRIVER_OPS_VMAP,
.debugfs_init = mipi_dbi_debugfs_init,
.name = "ili9341",
.desc = "Ilitek ILI9341",
.date = "20210716",
.major = 1,
.minor = 0,
};
static int ili9341_dbi_probe(struct spi_device *spi, struct gpio_desc *dc,
struct gpio_desc *reset)
{
struct device *dev = &spi->dev;
struct mipi_dbi_dev *dbidev;
struct mipi_dbi *dbi;
struct drm_device *drm;
struct regulator *vcc;
u32 rotation = 0;
int ret;
vcc = devm_regulator_get_optional(dev, "vcc");
if (IS_ERR(vcc)) {
dev_err(dev, "get optional vcc failed\n");
vcc = NULL;
}
dbidev = devm_drm_dev_alloc(dev, &ili9341_dbi_driver,
struct mipi_dbi_dev, drm);
if (IS_ERR(dbidev))
return PTR_ERR(dbidev);
dbi = &dbidev->dbi;
drm = &dbidev->drm;
dbi->reset = reset;
dbidev->regulator = vcc;
drm_mode_config_init(drm);
dbidev->backlight = devm_of_find_backlight(dev);
if (IS_ERR(dbidev->backlight))
return PTR_ERR(dbidev->backlight);
device_property_read_u32(dev, "rotation", &rotation);
ret = mipi_dbi_spi_init(spi, dbi, dc);
if (ret)
return ret;
ret = mipi_dbi_dev_init(dbidev, &ili9341_dbi_funcs,
&ili9341_dbi_mode, rotation);
if (ret)
return ret;
drm_mode_config_reset(drm);
ret = drm_dev_register(drm, 0);
if (ret)
return ret;
spi_set_drvdata(spi, drm);
drm_fbdev_dma_setup(drm, 0);
return 0;
}
static int ili9341_dpi_probe(struct spi_device *spi, struct gpio_desc *dc,
struct gpio_desc *reset)
{
@ -711,7 +538,6 @@ static int ili9341_probe(struct spi_device *spi)
struct device *dev = &spi->dev;
struct gpio_desc *dc;
struct gpio_desc *reset;
const struct spi_device_id *id = spi_get_device_id(spi);
reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(reset))
@ -721,36 +547,15 @@ static int ili9341_probe(struct spi_device *spi)
if (IS_ERR(dc))
return dev_err_probe(dev, PTR_ERR(dc), "Failed to get gpio 'dc'\n");
if (!strcmp(id->name, "sf-tc240t-9370-t"))
return ili9341_dpi_probe(spi, dc, reset);
if (!strcmp(id->name, "yx240qv29"))
return ili9341_dbi_probe(spi, dc, reset);
return -ENODEV;
return ili9341_dpi_probe(spi, dc, reset);
}
static void ili9341_remove(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct ili9341 *ili = spi_get_drvdata(spi);
struct drm_device *drm = spi_get_drvdata(spi);
if (!strcmp(id->name, "sf-tc240t-9370-t")) {
ili9341_dpi_power_off(ili);
drm_panel_remove(&ili->panel);
} else if (!strcmp(id->name, "yx240qv29")) {
drm_dev_unplug(drm);
drm_atomic_helper_shutdown(drm);
}
}
static void ili9341_shutdown(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
if (!strcmp(id->name, "yx240qv29"))
drm_atomic_helper_shutdown(spi_get_drvdata(spi));
ili9341_dpi_power_off(ili);
drm_panel_remove(&ili->panel);
}
static const struct of_device_id ili9341_of_match[] = {
@ -758,19 +563,11 @@ static const struct of_device_id ili9341_of_match[] = {
.compatible = "st,sf-tc240t-9370-t",
.data = &ili9341_stm32f429_disco_data,
},
{
/* porting from tiny/ili9341.c
* for original mipi dbi compitable
*/
.compatible = "adafruit,yx240qv29",
.data = NULL,
},
{ }
};
MODULE_DEVICE_TABLE(of, ili9341_of_match);
static const struct spi_device_id ili9341_id[] = {
{ "yx240qv29", 0 },
{ "sf-tc240t-9370-t", 0 },
{ }
};
@ -779,7 +576,6 @@ MODULE_DEVICE_TABLE(spi, ili9341_id);
static struct spi_driver ili9341_driver = {
.probe = ili9341_probe,
.remove = ili9341_remove,
.shutdown = ili9341_shutdown,
.id_table = ili9341_id,
.driver = {
.name = "panel-ilitek-ili9341",

View File

@ -617,12 +617,12 @@ static const struct khadas_ts050_panel_cmd ts050_init_code[] = {
{0xd4, {0x04}, 0x01}, /* RGBMIPICTRL: VSYNC front porch = 4 */
};
struct khadas_ts050_panel_data ts050_panel_data = {
static struct khadas_ts050_panel_data ts050_panel_data = {
.init_code = (struct khadas_ts050_panel_cmd *)ts050_init_code,
.len = ARRAY_SIZE(ts050_init_code)
};
struct khadas_ts050_panel_data ts050v2_panel_data = {
static struct khadas_ts050_panel_data ts050v2_panel_data = {
.init_code = (struct khadas_ts050_panel_cmd *)ts050v2_init_code,
.len = ARRAY_SIZE(ts050v2_init_code)
};

View File

@ -1095,18 +1095,6 @@ static int nt36523_unprepare(struct drm_panel *panel)
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);
}
@ -1251,7 +1239,7 @@ static int nt36523_probe(struct mipi_dsi_device *dsi)
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);
pinfo->dsi[1] = devm_mipi_dsi_device_register_full(dev, dsi1_host, info);
if (IS_ERR(pinfo->dsi[1])) {
dev_err(dev, "cannot get secondary DSI device\n");
return PTR_ERR(pinfo->dsi[1]);
@ -1288,7 +1276,7 @@ static int nt36523_probe(struct mipi_dsi_device *dsi)
pinfo->dsi[i]->format = pinfo->desc->format;
pinfo->dsi[i]->mode_flags = pinfo->desc->mode_flags;
ret = mipi_dsi_attach(pinfo->dsi[i]);
ret = devm_mipi_dsi_attach(dev, pinfo->dsi[i]);
if (ret < 0)
return dev_err_probe(dev, ret, "cannot attach to DSI%d host.\n", i);
}

View File

@ -46,108 +46,73 @@ static void rm69380_reset(struct rm69380_panel *ctx)
static int rm69380_on(struct rm69380_panel *ctx)
{
struct mipi_dsi_device *dsi = ctx->dsi[0];
struct device *dev = &dsi->dev;
int ret;
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
if (ctx->dsi[1])
ctx->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;
mipi_dsi_dcs_write_seq(dsi, 0xfe, 0xd4);
mipi_dsi_dcs_write_seq(dsi, 0x00, 0x80);
mipi_dsi_dcs_write_seq(dsi, 0xfe, 0xd0);
mipi_dsi_dcs_write_seq(dsi, 0x48, 0x00);
mipi_dsi_dcs_write_seq(dsi, 0xfe, 0x26);
mipi_dsi_dcs_write_seq(dsi, 0x75, 0x3f);
mipi_dsi_dcs_write_seq(dsi, 0x1d, 0x1a);
mipi_dsi_dcs_write_seq(dsi, 0xfe, 0x00);
mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x28);
mipi_dsi_dcs_write_seq(dsi, 0xc2, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0xd4);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x80);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0xd0);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x26);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x3f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1d, 0x1a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x28);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc2, 0x08);
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_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 20);
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(20);
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 36);
ret = mipi_dsi_dcs_set_display_on(dsi);
if (ret < 0) {
dev_err(dev, "Failed to set display on: %d\n", ret);
return ret;
}
msleep(36);
return 0;
return dsi_ctx.accum_err;
}
static int rm69380_off(struct rm69380_panel *ctx)
static void rm69380_off(struct rm69380_panel *ctx)
{
struct mipi_dsi_device *dsi = ctx->dsi[0];
struct device *dev = &dsi->dev;
int ret;
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
if (ctx->dsi[1])
ctx->dsi[1]->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(35);
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(20);
return 0;
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 35);
mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 20);
}
static int rm69380_prepare(struct drm_panel *panel)
{
struct rm69380_panel *ctx = to_rm69380_panel(panel);
struct device *dev = &ctx->dsi[0]->dev;
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
if (ret < 0) {
dev_err(dev, "Failed to enable regulators: %d\n", ret);
if (ret < 0)
return ret;
}
rm69380_reset(ctx);
ret = rm69380_on(ctx);
if (ret < 0) {
dev_err(dev, "Failed to initialize panel: %d\n", ret);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
return ret;
}
return 0;
return ret;
}
static int rm69380_unprepare(struct drm_panel *panel)
{
struct rm69380_panel *ctx = to_rm69380_panel(panel);
struct device *dev = &ctx->dsi[0]->dev;
int ret;
ret = rm69380_off(ctx);
if (ret < 0)
dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
rm69380_off(ctx);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);

View File

@ -38,57 +38,38 @@ static void s6e3fa7_panel_reset(struct s6e3fa7_panel *ctx)
usleep_range(10000, 11000);
}
static int s6e3fa7_panel_on(struct s6e3fa7_panel *ctx)
static int s6e3fa7_panel_on(struct mipi_dsi_device *dsi)
{
struct mipi_dsi_device *dsi = ctx->dsi;
struct device *dev = &dsi->dev;
int ret;
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
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(120);
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 120);
mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
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_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf4,
0xbb, 0x23, 0x19, 0x3a, 0x9f, 0x0f, 0x09, 0xc0,
0x00, 0xb4, 0x37, 0x70, 0x79, 0x69);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
mipi_dsi_dcs_write_seq(dsi, 0xf4,
0xbb, 0x23, 0x19, 0x3a, 0x9f, 0x0f, 0x09, 0xc0,
0x00, 0xb4, 0x37, 0x70, 0x79, 0x69);
mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
ret = mipi_dsi_dcs_set_display_on(dsi);
if (ret < 0) {
dev_err(dev, "Failed to set display on: %d\n", ret);
return ret;
}
return 0;
return dsi_ctx.accum_err;
}
static int s6e3fa7_panel_prepare(struct drm_panel *panel)
{
struct s6e3fa7_panel *ctx = to_s6e3fa7_panel(panel);
struct device *dev = &ctx->dsi->dev;
int ret;
s6e3fa7_panel_reset(ctx);
ret = s6e3fa7_panel_on(ctx);
if (ret < 0) {
dev_err(dev, "Failed to initialize panel: %d\n", ret);
ret = s6e3fa7_panel_on(ctx->dsi);
if (ret < 0)
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
return ret;
}
return 0;
return ret;
}
static int s6e3fa7_panel_unprepare(struct drm_panel *panel)
@ -104,23 +85,13 @@ static int s6e3fa7_panel_disable(struct drm_panel *panel)
{
struct s6e3fa7_panel *ctx = to_s6e3fa7_panel(panel);
struct mipi_dsi_device *dsi = ctx->dsi;
struct device *dev = &dsi->dev;
int ret;
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
ret = mipi_dsi_dcs_set_display_off(dsi);
if (ret < 0) {
dev_err(dev, "Failed to set display off: %d\n", ret);
return ret;
}
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 120);
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(120);
return 0;
return dsi_ctx.accum_err;
}
static const struct drm_display_mode s6e3fa7_panel_mode = {

View File

@ -562,8 +562,7 @@ static int acx565akm_detect(struct acx565akm_panel *lcd)
lcd->enabled ? "enabled" : "disabled ", status);
acx565akm_read(lcd, MIPI_DCS_GET_DISPLAY_ID, lcd->display_id, 3);
dev_dbg(&lcd->spi->dev, "MIPI display ID: %02x%02x%02x\n",
lcd->display_id[0], lcd->display_id[1], lcd->display_id[2]);
dev_dbg(&lcd->spi->dev, "MIPI display ID: %3phN\n", lcd->display_id);
switch (lcd->display_id[0]) {
case 0x10:

View File

@ -3,6 +3,10 @@
/* Copyright 2019 Linaro, Ltd., Rob Herring <robh@kernel.org> */
/* Copyright 2019 Collabora ltd. */
#ifdef CONFIG_ARM_ARCH_TIMER
#include <asm/arch_timer.h>
#endif
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pagemap.h>
@ -21,13 +25,33 @@
#include "panfrost_gpu.h"
#include "panfrost_perfcnt.h"
#define JOB_REQUIREMENTS (PANFROST_JD_REQ_FS | PANFROST_JD_REQ_CYCLE_COUNT)
static bool unstable_ioctls;
module_param_unsafe(unstable_ioctls, bool, 0600);
static int panfrost_ioctl_query_timestamp(struct panfrost_device *pfdev,
u64 *arg)
{
int ret;
ret = pm_runtime_resume_and_get(pfdev->dev);
if (ret)
return ret;
panfrost_cycle_counter_get(pfdev);
*arg = panfrost_timestamp_read(pfdev);
panfrost_cycle_counter_put(pfdev);
pm_runtime_put(pfdev->dev);
return 0;
}
static int panfrost_ioctl_get_param(struct drm_device *ddev, void *data, struct drm_file *file)
{
struct drm_panfrost_get_param *param = data;
struct panfrost_device *pfdev = ddev->dev_private;
int ret;
if (param->pad != 0)
return -EINVAL;
@ -69,6 +93,21 @@ static int panfrost_ioctl_get_param(struct drm_device *ddev, void *data, struct
PANFROST_FEATURE_ARRAY(JS_FEATURES, js_features, 15);
PANFROST_FEATURE(NR_CORE_GROUPS, nr_core_groups);
PANFROST_FEATURE(THREAD_TLS_ALLOC, thread_tls_alloc);
case DRM_PANFROST_PARAM_SYSTEM_TIMESTAMP:
ret = panfrost_ioctl_query_timestamp(pfdev, &param->value);
if (ret)
return ret;
break;
case DRM_PANFROST_PARAM_SYSTEM_TIMESTAMP_FREQUENCY:
#ifdef CONFIG_ARM_ARCH_TIMER
param->value = arch_timer_get_cntfrq();
#else
param->value = 0;
#endif
break;
default:
return -EINVAL;
}
@ -245,7 +284,7 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data,
if (!args->jc)
return -EINVAL;
if (args->requirements && args->requirements != PANFROST_JD_REQ_FS)
if (args->requirements & ~JOB_REQUIREMENTS)
return -EINVAL;
if (args->out_sync > 0) {
@ -584,6 +623,8 @@ static const struct file_operations panfrost_drm_driver_fops = {
* - 1.0 - initial interface
* - 1.1 - adds HEAP and NOEXEC flags for CREATE_BO
* - 1.2 - adds AFBC_FEATURES query
* - 1.3 - adds JD_REQ_CYCLE_COUNT job requirement for SUBMIT
* - adds SYSTEM_TIMESTAMP and SYSTEM_TIMESTAMP_FREQUENCY queries
*/
static const struct drm_driver panfrost_drm_driver = {
.driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ,
@ -597,7 +638,7 @@ static const struct drm_driver panfrost_drm_driver = {
.desc = "panfrost DRM",
.date = "20180908",
.major = 1,
.minor = 2,
.minor = 3,
.gem_create_object = panfrost_gem_create_object,
.gem_prime_import_sg_table = panfrost_gem_prime_import_sg_table,

View File

@ -380,6 +380,18 @@ unsigned long long panfrost_cycle_counter_read(struct panfrost_device *pfdev)
return ((u64)hi << 32) | lo;
}
unsigned long long panfrost_timestamp_read(struct panfrost_device *pfdev)
{
u32 hi, lo;
do {
hi = gpu_read(pfdev, GPU_TIMESTAMP_HI);
lo = gpu_read(pfdev, GPU_TIMESTAMP_LO);
} while (hi != gpu_read(pfdev, GPU_TIMESTAMP_HI));
return ((u64)hi << 32) | lo;
}
static u64 panfrost_get_core_mask(struct panfrost_device *pfdev)
{
u64 core_mask;

View File

@ -20,6 +20,7 @@ void panfrost_gpu_suspend_irq(struct panfrost_device *pfdev);
void panfrost_cycle_counter_get(struct panfrost_device *pfdev);
void panfrost_cycle_counter_put(struct panfrost_device *pfdev);
unsigned long long panfrost_cycle_counter_read(struct panfrost_device *pfdev);
unsigned long long panfrost_timestamp_read(struct panfrost_device *pfdev);
void panfrost_gpu_amlogic_quirk(struct panfrost_device *pfdev);

View File

@ -159,16 +159,17 @@ panfrost_dequeue_job(struct panfrost_device *pfdev, int slot)
struct panfrost_job *job = pfdev->jobs[slot][0];
WARN_ON(!job);
if (job->is_profiled) {
if (job->engine_usage) {
job->engine_usage->elapsed_ns[slot] +=
ktime_to_ns(ktime_sub(ktime_get(), job->start_time));
job->engine_usage->cycles[slot] +=
panfrost_cycle_counter_read(pfdev) - job->start_cycles;
}
panfrost_cycle_counter_put(job->pfdev);
if (job->is_profiled && job->engine_usage) {
job->engine_usage->elapsed_ns[slot] +=
ktime_to_ns(ktime_sub(ktime_get(), job->start_time));
job->engine_usage->cycles[slot] +=
panfrost_cycle_counter_read(pfdev) - job->start_cycles;
}
if (job->requirements & PANFROST_JD_REQ_CYCLE_COUNT || job->is_profiled)
panfrost_cycle_counter_put(pfdev);
pfdev->jobs[slot][0] = pfdev->jobs[slot][1];
pfdev->jobs[slot][1] = NULL;
@ -243,9 +244,13 @@ static void panfrost_job_hw_submit(struct panfrost_job *job, int js)
subslot = panfrost_enqueue_job(pfdev, js, job);
/* Don't queue the job if a reset is in progress */
if (!atomic_read(&pfdev->reset.pending)) {
if (pfdev->profile_mode) {
job->is_profiled = pfdev->profile_mode;
if (job->requirements & PANFROST_JD_REQ_CYCLE_COUNT ||
job->is_profiled)
panfrost_cycle_counter_get(pfdev);
job->is_profiled = true;
if (job->is_profiled) {
job->start_time = ktime_get();
job->start_cycles = panfrost_cycle_counter_read(pfdev);
}
@ -693,7 +698,8 @@ panfrost_reset(struct panfrost_device *pfdev,
spin_lock(&pfdev->js->job_lock);
for (i = 0; i < NUM_JOB_SLOTS; i++) {
for (j = 0; j < ARRAY_SIZE(pfdev->jobs[0]) && pfdev->jobs[i][j]; j++) {
if (pfdev->jobs[i][j]->is_profiled)
if (pfdev->jobs[i][j]->requirements & PANFROST_JD_REQ_CYCLE_COUNT ||
pfdev->jobs[i][j]->is_profiled)
panfrost_cycle_counter_put(pfdev->jobs[i][j]->pfdev);
pm_runtime_put_noidle(pfdev->dev);
panfrost_devfreq_record_idle(&pfdev->pfdevfreq);
@ -727,7 +733,7 @@ panfrost_reset(struct panfrost_device *pfdev,
/* Restart the schedulers */
for (i = 0; i < NUM_JOB_SLOTS; i++)
drm_sched_start(&pfdev->js->queue[i].sched);
drm_sched_start(&pfdev->js->queue[i].sched, 0);
/* Re-enable job interrupts now that everything has been restarted. */
job_write(pfdev, JOB_INT_MASK,

View File

@ -78,6 +78,8 @@
#define GPU_CYCLE_COUNT_LO 0x90
#define GPU_CYCLE_COUNT_HI 0x94
#define GPU_TIMESTAMP_LO 0x98
#define GPU_TIMESTAMP_HI 0x9C
#define GPU_THREAD_MAX_THREADS 0x0A0 /* (RO) Maximum number of threads per core */
#define GPU_THREAD_MAX_WORKGROUP_SIZE 0x0A4 /* (RO) Maximum workgroup size */

View File

@ -3,6 +3,10 @@
/* Copyright 2019 Linaro, Ltd., Rob Herring <robh@kernel.org> */
/* Copyright 2019 Collabora ltd. */
#ifdef CONFIG_ARM_ARCH_TIMER
#include <asm/arch_timer.h>
#endif
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of_platform.h>
@ -165,6 +169,7 @@ panthor_get_uobj_array(const struct drm_panthor_obj_array *in, u32 min_stride,
_Generic(_obj_name, \
PANTHOR_UOBJ_DECL(struct drm_panthor_gpu_info, tiler_present), \
PANTHOR_UOBJ_DECL(struct drm_panthor_csif_info, pad), \
PANTHOR_UOBJ_DECL(struct drm_panthor_timestamp_info, current_timestamp), \
PANTHOR_UOBJ_DECL(struct drm_panthor_sync_op, timeline_value), \
PANTHOR_UOBJ_DECL(struct drm_panthor_queue_submit, syncs), \
PANTHOR_UOBJ_DECL(struct drm_panthor_queue_create, ringbuf_size), \
@ -751,10 +756,33 @@ static void panthor_submit_ctx_cleanup(struct panthor_submit_ctx *ctx,
kvfree(ctx->jobs);
}
static int panthor_query_timestamp_info(struct panthor_device *ptdev,
struct drm_panthor_timestamp_info *arg)
{
int ret;
ret = pm_runtime_resume_and_get(ptdev->base.dev);
if (ret)
return ret;
#ifdef CONFIG_ARM_ARCH_TIMER
arg->timestamp_frequency = arch_timer_get_cntfrq();
#else
arg->timestamp_frequency = 0;
#endif
arg->current_timestamp = panthor_gpu_read_timestamp(ptdev);
arg->timestamp_offset = panthor_gpu_read_timestamp_offset(ptdev);
pm_runtime_put(ptdev->base.dev);
return 0;
}
static int panthor_ioctl_dev_query(struct drm_device *ddev, void *data, struct drm_file *file)
{
struct panthor_device *ptdev = container_of(ddev, struct panthor_device, base);
struct drm_panthor_dev_query *args = data;
struct drm_panthor_timestamp_info timestamp_info;
int ret;
if (!args->pointer) {
switch (args->type) {
@ -766,6 +794,10 @@ static int panthor_ioctl_dev_query(struct drm_device *ddev, void *data, struct d
args->size = sizeof(ptdev->csif_info);
return 0;
case DRM_PANTHOR_DEV_QUERY_TIMESTAMP_INFO:
args->size = sizeof(timestamp_info);
return 0;
default:
return -EINVAL;
}
@ -778,6 +810,14 @@ static int panthor_ioctl_dev_query(struct drm_device *ddev, void *data, struct d
case DRM_PANTHOR_DEV_QUERY_CSIF_INFO:
return PANTHOR_UOBJ_SET(args->pointer, args->size, ptdev->csif_info);
case DRM_PANTHOR_DEV_QUERY_TIMESTAMP_INFO:
ret = panthor_query_timestamp_info(ptdev, &timestamp_info);
if (ret)
return ret;
return PANTHOR_UOBJ_SET(args->pointer, args->size, timestamp_info);
default:
return -EINVAL;
}
@ -1396,6 +1436,7 @@ static void panthor_debugfs_init(struct drm_minor *minor)
/*
* PanCSF driver version:
* - 1.0 - initial interface
* - 1.1 - adds DEV_QUERY_TIMESTAMP_INFO query
*/
static const struct drm_driver panthor_drm_driver = {
.driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ |
@ -1409,7 +1450,7 @@ static const struct drm_driver panthor_drm_driver = {
.desc = "Panthor DRM driver",
.date = "20230801",
.major = 1,
.minor = 0,
.minor = 1,
.gem_create_object = panthor_gem_create_object,
.gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table,

View File

@ -78,6 +78,12 @@ enum panthor_fw_binary_entry_type {
/** @CSF_FW_BINARY_ENTRY_TYPE_TIMELINE_METADATA: Timeline metadata interface. */
CSF_FW_BINARY_ENTRY_TYPE_TIMELINE_METADATA = 4,
/**
* @CSF_FW_BINARY_ENTRY_TYPE_BUILD_INFO_METADATA: Metadata about how
* the FW binary was built.
*/
CSF_FW_BINARY_ENTRY_TYPE_BUILD_INFO_METADATA = 6
};
#define CSF_FW_BINARY_ENTRY_TYPE(ehdr) ((ehdr) & 0xff)
@ -132,6 +138,13 @@ struct panthor_fw_binary_section_entry_hdr {
} data;
};
struct panthor_fw_build_info_hdr {
/** @meta_start: Offset of the build info data in the FW binary */
u32 meta_start;
/** @meta_size: Size of the build info data in the FW binary */
u32 meta_size;
};
/**
* struct panthor_fw_binary_iter - Firmware binary iterator
*
@ -628,6 +641,46 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
return 0;
}
static int panthor_fw_read_build_info(struct panthor_device *ptdev,
const struct firmware *fw,
struct panthor_fw_binary_iter *iter,
u32 ehdr)
{
struct panthor_fw_build_info_hdr hdr;
char header[9];
const char git_sha_header[sizeof(header)] = "git_sha: ";
int ret;
ret = panthor_fw_binary_iter_read(ptdev, iter, &hdr, sizeof(hdr));
if (ret)
return ret;
if (hdr.meta_start > fw->size ||
hdr.meta_start + hdr.meta_size > fw->size) {
drm_err(&ptdev->base, "Firmware build info corrupt\n");
/* We don't need the build info, so continue */
return 0;
}
if (memcmp(git_sha_header, fw->data + hdr.meta_start,
sizeof(git_sha_header))) {
/* Not the expected header, this isn't metadata we understand */
return 0;
}
/* Check that the git SHA is NULL terminated as expected */
if (fw->data[hdr.meta_start + hdr.meta_size - 1] != '\0') {
drm_warn(&ptdev->base, "Firmware's git sha is not NULL terminated\n");
/* Don't treat as fatal */
return 0;
}
drm_info(&ptdev->base, "Firmware git sha: %s\n",
fw->data + hdr.meta_start + sizeof(git_sha_header));
return 0;
}
static void
panthor_reload_fw_sections(struct panthor_device *ptdev, bool full_reload)
{
@ -672,6 +725,8 @@ static int panthor_fw_load_entry(struct panthor_device *ptdev,
switch (CSF_FW_BINARY_ENTRY_TYPE(ehdr)) {
case CSF_FW_BINARY_ENTRY_TYPE_IFACE:
return panthor_fw_load_section_entry(ptdev, fw, &eiter, ehdr);
case CSF_FW_BINARY_ENTRY_TYPE_BUILD_INFO_METADATA:
return panthor_fw_read_build_info(ptdev, fw, &eiter, ehdr);
/* FIXME: handle those entry types? */
case CSF_FW_BINARY_ENTRY_TYPE_CONFIG:
@ -921,7 +976,7 @@ static int panthor_fw_init_ifaces(struct panthor_device *ptdev)
return ret;
}
drm_info(&ptdev->base, "CSF FW v%d.%d.%d, Features %#x Instrumentation features %#x",
drm_info(&ptdev->base, "CSF FW using interface v%d.%d.%d, Features %#x Instrumentation features %#x",
CSF_IFACE_VERSION_MAJOR(glb_iface->control->version),
CSF_IFACE_VERSION_MINOR(glb_iface->control->version),
CSF_IFACE_VERSION_PATCH(glb_iface->control->version),

View File

@ -480,3 +480,50 @@ void panthor_gpu_resume(struct panthor_device *ptdev)
panthor_gpu_irq_resume(&ptdev->gpu->irq, GPU_INTERRUPTS_MASK);
panthor_gpu_l2_power_on(ptdev);
}
/**
* panthor_gpu_read_64bit_counter() - Read a 64-bit counter at a given offset.
* @ptdev: Device.
* @reg: The offset of the register to read.
*
* Return: The counter value.
*/
static u64
panthor_gpu_read_64bit_counter(struct panthor_device *ptdev, u32 reg)
{
u32 hi, lo;
do {
hi = gpu_read(ptdev, reg + 0x4);
lo = gpu_read(ptdev, reg);
} while (hi != gpu_read(ptdev, reg + 0x4));
return ((u64)hi << 32) | lo;
}
/**
* panthor_gpu_read_timestamp() - Read the timestamp register.
* @ptdev: Device.
*
* Return: The GPU timestamp value.
*/
u64 panthor_gpu_read_timestamp(struct panthor_device *ptdev)
{
return panthor_gpu_read_64bit_counter(ptdev, GPU_TIMESTAMP_LO);
}
/**
* panthor_gpu_read_timestamp_offset() - Read the timestamp offset register.
* @ptdev: Device.
*
* Return: The GPU timestamp offset value.
*/
u64 panthor_gpu_read_timestamp_offset(struct panthor_device *ptdev)
{
u32 hi, lo;
hi = gpu_read(ptdev, GPU_TIMESTAMP_OFFSET_HI);
lo = gpu_read(ptdev, GPU_TIMESTAMP_OFFSET_LO);
return ((u64)hi << 32) | lo;
}

View File

@ -5,6 +5,8 @@
#ifndef __PANTHOR_GPU_H__
#define __PANTHOR_GPU_H__
#include <linux/types.h>
struct panthor_device;
int panthor_gpu_init(struct panthor_device *ptdev);
@ -48,5 +50,7 @@ int panthor_gpu_l2_power_on(struct panthor_device *ptdev);
int panthor_gpu_flush_caches(struct panthor_device *ptdev,
u32 l2, u32 lsc, u32 other);
int panthor_gpu_soft_reset(struct panthor_device *ptdev);
u64 panthor_gpu_read_timestamp(struct panthor_device *ptdev);
u64 panthor_gpu_read_timestamp_offset(struct panthor_device *ptdev);
#endif

View File

@ -833,7 +833,7 @@ static void panthor_vm_stop(struct panthor_vm *vm)
static void panthor_vm_start(struct panthor_vm *vm)
{
drm_sched_start(&vm->sched);
drm_sched_start(&vm->sched, 0);
}
/**
@ -2716,9 +2716,9 @@ int panthor_mmu_init(struct panthor_device *ptdev)
* which passes iova as an unsigned long. Patch the mmu_features to reflect this
* limitation.
*/
if (sizeof(unsigned long) * 8 < va_bits) {
if (va_bits > BITS_PER_LONG) {
ptdev->gpu_info.mmu_features &= ~GENMASK(7, 0);
ptdev->gpu_info.mmu_features |= sizeof(unsigned long) * 8;
ptdev->gpu_info.mmu_features |= BITS_PER_LONG;
}
return drmm_add_action_or_reset(&ptdev->base, panthor_mmu_release_wq, mmu->vm.wq);

View File

@ -2545,7 +2545,7 @@ static void queue_start(struct panthor_queue *queue)
list_for_each_entry(job, &queue->scheduler.pending_list, base.list)
job->base.s_fence->parent = dma_fence_get(job->done_fence);
drm_sched_start(&queue->scheduler);
drm_sched_start(&queue->scheduler, 0);
}
static void panthor_group_stop(struct panthor_group *group)

View File

@ -77,7 +77,7 @@
#define SOURCE_PIF_PKT_ALLOC_WR_EN 0x30830
#define SOURCE_PIF_SW_RESET 0x30834
/* bellow registers need access by mailbox */
/* below registers need access by mailbox */
/* source car addr */
#define SOURCE_HDTX_CAR 0x0900
#define SOURCE_DPTX_CAR 0x0904

View File

@ -76,6 +76,7 @@ struct rockchip_hdmi {
struct rockchip_encoder encoder;
const struct rockchip_hdmi_chip_data *chip_data;
const struct dw_hdmi_plat_data *plat_data;
struct clk *hdmiphy_clk;
struct clk *ref_clk;
struct clk *grf_clk;
struct dw_hdmi *hdmi;
@ -91,74 +92,70 @@ static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
{
27000000, {
{ 0x00b3, 0x0000},
{ 0x2153, 0x0000},
{ 0x40f3, 0x0000}
30666000, {
{ 0x00b3, 0x0000 },
{ 0x2153, 0x0000 },
{ 0x40f3, 0x0000 },
},
}, {
36000000, {
{ 0x00b3, 0x0000},
{ 0x2153, 0x0000},
{ 0x40f3, 0x0000}
36800000, {
{ 0x00b3, 0x0000 },
{ 0x2153, 0x0000 },
{ 0x40a2, 0x0001 },
},
}, {
40000000, {
{ 0x00b3, 0x0000},
{ 0x2153, 0x0000},
{ 0x40f3, 0x0000}
46000000, {
{ 0x00b3, 0x0000 },
{ 0x2142, 0x0001 },
{ 0x40a2, 0x0001 },
},
}, {
54000000, {
{ 0x0072, 0x0001},
{ 0x2142, 0x0001},
{ 0x40a2, 0x0001},
61333000, {
{ 0x0072, 0x0001 },
{ 0x2142, 0x0001 },
{ 0x40a2, 0x0001 },
},
}, {
65000000, {
{ 0x0072, 0x0001},
{ 0x2142, 0x0001},
{ 0x40a2, 0x0001},
73600000, {
{ 0x0072, 0x0001 },
{ 0x2142, 0x0001 },
{ 0x4061, 0x0002 },
},
}, {
66000000, {
{ 0x013e, 0x0003},
{ 0x217e, 0x0002},
{ 0x4061, 0x0002}
92000000, {
{ 0x0072, 0x0001 },
{ 0x2145, 0x0002 },
{ 0x4061, 0x0002 },
},
}, {
74250000, {
{ 0x0072, 0x0001},
{ 0x2145, 0x0002},
{ 0x4061, 0x0002}
122666000, {
{ 0x0051, 0x0002 },
{ 0x2145, 0x0002 },
{ 0x4061, 0x0002 },
},
}, {
83500000, {
{ 0x0072, 0x0001},
147200000, {
{ 0x0051, 0x0002 },
{ 0x2145, 0x0002 },
{ 0x4064, 0x0003 },
},
}, {
108000000, {
{ 0x0051, 0x0002},
{ 0x2145, 0x0002},
{ 0x4061, 0x0002}
184000000, {
{ 0x0051, 0x0002 },
{ 0x214c, 0x0003 },
{ 0x4064, 0x0003 },
},
}, {
106500000, {
{ 0x0051, 0x0002},
{ 0x2145, 0x0002},
{ 0x4061, 0x0002}
226666000, {
{ 0x0040, 0x0003 },
{ 0x214c, 0x0003 },
{ 0x4064, 0x0003 },
},
}, {
146250000, {
{ 0x0051, 0x0002},
{ 0x2145, 0x0002},
{ 0x4061, 0x0002}
},
}, {
148500000, {
{ 0x0051, 0x0003},
{ 0x214c, 0x0003},
{ 0x4064, 0x0003}
272000000, {
{ 0x0040, 0x0003 },
{ 0x214c, 0x0003 },
{ 0x5a64, 0x0003 },
},
}, {
340000000, {
@ -166,11 +163,17 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
{ 0x3b4c, 0x0003 },
{ 0x5a64, 0x0003 },
},
}, {
600000000, {
{ 0x1a40, 0x0003 },
{ 0x3b4c, 0x0003 },
{ 0x5a64, 0x0003 },
},
}, {
~0UL, {
{ 0x00a0, 0x000a },
{ 0x2001, 0x000f },
{ 0x4002, 0x000f },
{ 0x0000, 0x0000 },
{ 0x0000, 0x0000 },
{ 0x0000, 0x0000 },
},
}
};
@ -178,31 +181,18 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
/* pixelclk bpp8 bpp10 bpp12 */
{
40000000, { 0x0018, 0x0018, 0x0018 },
}, {
65000000, { 0x0028, 0x0028, 0x0028 },
}, {
66000000, { 0x0038, 0x0038, 0x0038 },
}, {
74250000, { 0x0028, 0x0038, 0x0038 },
}, {
83500000, { 0x0028, 0x0038, 0x0038 },
}, {
146250000, { 0x0038, 0x0038, 0x0038 },
}, {
148500000, { 0x0000, 0x0038, 0x0038 },
}, {
600000000, { 0x0000, 0x0000, 0x0000 },
}, {
~0UL, { 0x0000, 0x0000, 0x0000},
~0UL, { 0x0000, 0x0000, 0x0000 },
}
};
static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
/*pixelclk symbol term vlev*/
{ 74250000, 0x8009, 0x0004, 0x0272},
{ 148500000, 0x802b, 0x0004, 0x028d},
{ 165000000, 0x802b, 0x0004, 0x0209},
{ 297000000, 0x8039, 0x0005, 0x028d},
{ 594000000, 0x8039, 0x0000, 0x019d},
{ ~0UL, 0x0000, 0x0000, 0x0000}
};
@ -251,10 +241,7 @@ dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
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 exact_match = hdmi->plat_data->phy_force_vendor;
int i;
if (hdmi->chip_data->max_tmds_clock &&
mode->clock > hdmi->chip_data->max_tmds_clock)
@ -263,26 +250,18 @@ dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
if (hdmi->ref_clk) {
int rpclk = clk_round_rate(hdmi->ref_clk, pclk);
if (abs(rpclk - pclk) > pclk / 1000)
if (rpclk < 0 || abs(rpclk - pclk) > pclk / 1000)
return MODE_NOCLOCK;
}
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;
if (hdmi->hdmiphy_clk) {
int rpclk = clk_round_rate(hdmi->hdmiphy_clk, pclk);
if (rpclk < 0 || abs(rpclk - pclk) > pclk / 1000)
return MODE_NOCLOCK;
}
return MODE_BAD;
return MODE_OK;
}
static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
@ -502,7 +481,7 @@ static struct rockchip_hdmi_chip_data rk3399_chip_data = {
.lcdsel_grf_reg = RK3399_GRF_SOC_CON20,
.lcdsel_big = HIWORD_UPDATE(0, RK3399_HDMI_LCDC_SEL),
.lcdsel_lit = HIWORD_UPDATE(RK3399_HDMI_LCDC_SEL, RK3399_HDMI_LCDC_SEL),
.max_tmds_clock = 340000,
.max_tmds_clock = 594000,
};
static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
@ -516,7 +495,7 @@ static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
static struct rockchip_hdmi_chip_data rk3568_chip_data = {
.lcdsel_grf_reg = -1,
.max_tmds_clock = 340000,
.max_tmds_clock = 594000,
};
static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
@ -607,6 +586,15 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
return ret;
}
if (hdmi->phy) {
struct of_phandle_args clkspec;
clkspec.np = hdmi->phy->dev.of_node;
hdmi->hdmiphy_clk = of_clk_get_from_provider(&clkspec);
if (IS_ERR(hdmi->hdmiphy_clk))
hdmi->hdmiphy_clk = NULL;
}
if (hdmi->chip_data == &rk3568_chip_data) {
regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1,
HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK |

View File

@ -358,11 +358,34 @@ static void rockchip_drm_match_remove(struct device *dev)
device_link_del(link);
}
/* list of preferred vop devices */
static const char *const rockchip_drm_match_preferred[] = {
"rockchip,rk3399-vop-big",
NULL,
};
static struct component_match *rockchip_drm_match_add(struct device *dev)
{
struct component_match *match = NULL;
struct device_node *port;
int i;
/* add preferred vop device match before adding driver device matches */
for (i = 0; ; i++) {
port = of_parse_phandle(dev->of_node, "ports", i);
if (!port)
break;
if (of_device_is_available(port->parent) &&
of_device_compatible_match(port->parent,
rockchip_drm_match_preferred))
drm_of_component_match_add(dev, &match,
component_compare_of,
port->parent);
of_node_put(port);
}
for (i = 0; i < num_rockchip_sub_drivers; i++) {
struct platform_driver *drv = rockchip_sub_drivers[i];
struct device *p = NULL, *d;

View File

@ -674,9 +674,10 @@ EXPORT_SYMBOL(drm_sched_stop);
* drm_sched_start - recover jobs after a reset
*
* @sched: scheduler instance
* @errno: error to set on the pending fences
*
*/
void drm_sched_start(struct drm_gpu_scheduler *sched)
void drm_sched_start(struct drm_gpu_scheduler *sched, int errno)
{
struct drm_sched_job *s_job, *tmp;
@ -691,13 +692,13 @@ void drm_sched_start(struct drm_gpu_scheduler *sched)
atomic_add(s_job->credits, &sched->credit_count);
if (!fence) {
drm_sched_job_done(s_job, -ECANCELED);
drm_sched_job_done(s_job, errno ?: -ECANCELED);
continue;
}
if (dma_fence_add_callback(fence, &s_job->cb,
drm_sched_job_done_cb))
drm_sched_job_done(s_job, fence->error);
drm_sched_job_done(s_job, fence->error ?: errno);
}
drm_sched_start_timeout_unlocked(sched);

View File

@ -76,8 +76,8 @@ static struct host1x_bo_mapping *tegra_bo_pin(struct device *dev, struct host1x_
/*
* Imported buffers need special treatment to satisfy the semantics of DMA-BUF.
*/
if (gem->import_attach) {
struct dma_buf *buf = gem->import_attach->dmabuf;
if (obj->dma_buf) {
struct dma_buf *buf = obj->dma_buf;
map->attach = dma_buf_attach(buf, dev);
if (IS_ERR(map->attach)) {
@ -184,8 +184,8 @@ static void *tegra_bo_mmap(struct host1x_bo *bo)
if (obj->vaddr)
return obj->vaddr;
if (obj->gem.import_attach) {
ret = dma_buf_vmap_unlocked(obj->gem.import_attach->dmabuf, &map);
if (obj->dma_buf) {
ret = dma_buf_vmap_unlocked(obj->dma_buf, &map);
if (ret < 0)
return ERR_PTR(ret);
@ -208,8 +208,8 @@ static void tegra_bo_munmap(struct host1x_bo *bo, void *addr)
if (obj->vaddr)
return;
if (obj->gem.import_attach)
return dma_buf_vunmap_unlocked(obj->gem.import_attach->dmabuf, &map);
if (obj->dma_buf)
return dma_buf_vunmap_unlocked(obj->dma_buf, &map);
vunmap(addr);
}
@ -465,27 +465,32 @@ static struct tegra_bo *tegra_bo_import(struct drm_device *drm,
if (IS_ERR(bo))
return bo;
attach = dma_buf_attach(buf, drm->dev);
if (IS_ERR(attach)) {
err = PTR_ERR(attach);
goto free;
}
get_dma_buf(buf);
bo->sgt = dma_buf_map_attachment_unlocked(attach, DMA_TO_DEVICE);
if (IS_ERR(bo->sgt)) {
err = PTR_ERR(bo->sgt);
goto detach;
}
/*
* If we need to use IOMMU API to map the dma-buf into the internally managed
* domain, map it first to the DRM device to get an sgt.
*/
if (tegra->domain) {
attach = dma_buf_attach(buf, drm->dev);
if (IS_ERR(attach)) {
err = PTR_ERR(attach);
goto free;
}
bo->sgt = dma_buf_map_attachment_unlocked(attach, DMA_TO_DEVICE);
if (IS_ERR(bo->sgt)) {
err = PTR_ERR(bo->sgt);
goto detach;
}
err = tegra_bo_iommu_map(tegra, bo);
if (err < 0)
goto detach;
bo->gem.import_attach = attach;
}
bo->gem.import_attach = attach;
get_dma_buf(buf);
bo->dma_buf = buf;
return bo;
@ -516,17 +521,21 @@ void tegra_bo_free_object(struct drm_gem_object *gem)
dev_name(mapping->dev));
}
if (tegra->domain)
if (tegra->domain) {
tegra_bo_iommu_unmap(tegra, bo);
if (gem->import_attach) {
dma_buf_unmap_attachment_unlocked(gem->import_attach, bo->sgt,
DMA_TO_DEVICE);
drm_prime_gem_destroy(gem, NULL);
} else {
tegra_bo_free(gem->dev, bo);
if (gem->import_attach) {
dma_buf_unmap_attachment_unlocked(gem->import_attach, bo->sgt,
DMA_TO_DEVICE);
dma_buf_detach(gem->import_attach->dmabuf, gem->import_attach);
}
}
tegra_bo_free(gem->dev, bo);
if (bo->dma_buf)
dma_buf_put(bo->dma_buf);
drm_gem_object_release(gem);
kfree(bo);
}

View File

@ -32,6 +32,26 @@ struct tegra_bo_tiling {
enum tegra_bo_sector_layout sector_layout;
};
/*
* How memory is referenced within a tegra_bo:
*
* Buffer source | Mapping API(*) | Fields
* ---------------+-----------------+---------------
* Allocated here | DMA API | iova (IOVA mapped to drm->dev), vaddr (CPU VA)
*
* Allocated here | IOMMU API | pages/num_pages (Phys. memory), sgt (Mapped to drm->dev),
* | iova/size (Mapped to domain)
*
* Imported | DMA API | dma_buf (Imported dma_buf)
*
* Imported | IOMMU API | dma_buf (Imported dma_buf),
* | gem->import_attach (Attachment on drm->dev),
* | sgt (Mapped to drm->dev)
* | iova/size (Mapped to domain)
*
* (*) If tegra->domain is set, i.e. TegraDRM IOMMU domain is directly managed through IOMMU API,
* this is IOMMU API. Otherwise DMA API.
*/
struct tegra_bo {
struct drm_gem_object gem;
struct host1x_bo base;
@ -39,6 +59,7 @@ struct tegra_bo {
struct sg_table *sgt;
dma_addr_t iova;
void *vaddr;
struct dma_buf *dma_buf;
struct drm_mm_node *mm;
unsigned long num_pages;

View File

@ -46,7 +46,6 @@ struct gr3d {
unsigned int nclocks;
struct reset_control_bulk_data resets[RST_GR3D_MAX];
unsigned int nresets;
struct dev_pm_domain_list *pd_list;
DECLARE_BITMAP(addr_regs, GR3D_NUM_REGS);
};
@ -370,12 +369,18 @@ static int gr3d_power_up_legacy_domain(struct device *dev, const char *name,
return 0;
}
static void gr3d_del_link(void *link)
{
device_link_del(link);
}
static int gr3d_init_power(struct device *dev, struct gr3d *gr3d)
{
struct dev_pm_domain_attach_data pd_data = {
.pd_names = (const char *[]) { "3d0", "3d1" },
.num_pd_names = 2,
};
static const char * const opp_genpd_names[] = { "3d0", "3d1", NULL };
const u32 link_flags = DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME;
struct device **opp_virt_devs, *pd_dev;
struct device_link *link;
unsigned int i;
int err;
err = of_count_phandle_with_args(dev->of_node, "power-domains",
@ -409,10 +414,29 @@ static int gr3d_init_power(struct device *dev, struct gr3d *gr3d)
if (dev->pm_domain)
return 0;
err = dev_pm_domain_attach_list(dev, &pd_data, &gr3d->pd_list);
if (err < 0)
err = devm_pm_opp_attach_genpd(dev, opp_genpd_names, &opp_virt_devs);
if (err)
return err;
for (i = 0; opp_genpd_names[i]; i++) {
pd_dev = opp_virt_devs[i];
if (!pd_dev) {
dev_err(dev, "failed to get %s power domain\n",
opp_genpd_names[i]);
return -EINVAL;
}
link = device_link_add(dev, pd_dev, link_flags);
if (!link) {
dev_err(dev, "failed to link to %s\n", dev_name(pd_dev));
return -EINVAL;
}
err = devm_add_action_or_reset(dev, gr3d_del_link, link);
if (err)
return err;
}
return 0;
}
@ -503,13 +527,13 @@ static int gr3d_probe(struct platform_device *pdev)
err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
if (err)
goto err;
return err;
err = host1x_client_register(&gr3d->client.base);
if (err < 0) {
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
err);
goto err;
return err;
}
/* initialize address register map */
@ -517,9 +541,6 @@ static int gr3d_probe(struct platform_device *pdev)
set_bit(gr3d_addr_regs[i], gr3d->addr_regs);
return 0;
err:
dev_pm_domain_detach_list(gr3d->pd_list);
return err;
}
static void gr3d_remove(struct platform_device *pdev)
@ -528,7 +549,6 @@ static void gr3d_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
host1x_client_unregister(&gr3d->client.base);
dev_pm_domain_detach_list(gr3d->pd_list);
}
static int __maybe_unused gr3d_runtime_suspend(struct device *dev)

View File

@ -434,7 +434,7 @@ tegra_hdmi_get_audio_config(unsigned int audio_freq, unsigned int pix_clock,
static void tegra_hdmi_setup_audio_fs_tables(struct tegra_hdmi *hdmi)
{
const unsigned int freqs[] = {
static const unsigned int freqs[] = {
32000, 44100, 48000, 88200, 96000, 176400, 192000
};
unsigned int i;

View File

@ -5,11 +5,15 @@
* Copyright (c) 2022 Maíra Canal <mairacanal@riseup.net>
*/
#include <kunit/device.h>
#include <kunit/test.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_mode.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_kunit_helpers.h>
#include <drm/drm_print.h>
#include "../drm_crtc_internal.h"
@ -19,6 +23,8 @@
#define MIN_HEIGHT 4
#define MAX_HEIGHT 4096
#define DRM_MODE_FB_INVALID BIT(2)
struct drm_framebuffer_test {
int buffer_created;
struct drm_mode_fb_cmd2 cmd;
@ -83,6 +89,24 @@ static const struct drm_framebuffer_test drm_framebuffer_create_cases[] = {
.pitches = { 4 * MAX_WIDTH, 0, 0 },
}
},
/*
* All entries in members that represents per-plane values (@modifier, @handles,
* @pitches and @offsets) must be zero when unused.
*/
{ .buffer_created = 0, .name = "ABGR8888 Buffer offset for inexistent plane",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, UINT_MAX / 2, 0 },
.pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
}
},
{ .buffer_created = 0, .name = "ABGR8888 Invalid flag",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },
.pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_INVALID,
}
},
{ .buffer_created = 1, .name = "ABGR8888 Set DRM_MODE_FB_MODIFIERS without modifiers",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
.handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },
@ -262,6 +286,13 @@ static const struct drm_framebuffer_test drm_framebuffer_create_cases[] = {
.pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
}
},
{ .buffer_created = 0, .name = "YUV420_10BIT Invalid modifier(DRM_FORMAT_MOD_LINEAR)",
.cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YUV420_10BIT,
.handles = { 1, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
.modifier = { DRM_FORMAT_MOD_LINEAR, 0, 0 },
.pitches = { MAX_WIDTH, 0, 0 },
}
},
{ .buffer_created = 1, .name = "X0L2 Normal sizes",
.cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_X0L2,
.handles = { 1, 0, 0 }, .pitches = { 1200, 0, 0 }
@ -317,12 +348,26 @@ static const struct drm_framebuffer_test drm_framebuffer_create_cases[] = {
},
};
/*
* This struct is intended to provide a way to mocked functions communicate
* with the outer test when it can't be achieved by using its return value. In
* this way, the functions that receive the mocked drm_device, for example, can
* grab a reference to this and actually return something to be used on some
* expectation.
*/
struct drm_framebuffer_test_priv {
struct drm_device dev;
bool buffer_created;
bool buffer_freed;
};
static struct drm_framebuffer *fb_create_mock(struct drm_device *dev,
struct drm_file *file_priv,
const struct drm_mode_fb_cmd2 *mode_cmd)
{
int *buffer_created = dev->dev_private;
*buffer_created = 1;
struct drm_framebuffer_test_priv *priv = container_of(dev, typeof(*priv), dev);
priv->buffer_created = true;
return ERR_PTR(-EINVAL);
}
@ -332,42 +377,338 @@ static struct drm_mode_config_funcs mock_config_funcs = {
static int drm_framebuffer_test_init(struct kunit *test)
{
struct drm_device *mock;
struct device *parent;
struct drm_framebuffer_test_priv *priv;
struct drm_device *dev;
mock = kunit_kzalloc(test, sizeof(*mock), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mock);
parent = drm_kunit_helper_alloc_device(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
mock->mode_config.min_width = MIN_WIDTH;
mock->mode_config.max_width = MAX_WIDTH;
mock->mode_config.min_height = MIN_HEIGHT;
mock->mode_config.max_height = MAX_HEIGHT;
mock->mode_config.funcs = &mock_config_funcs;
priv = drm_kunit_helper_alloc_drm_device(test, parent, typeof(*priv),
dev, 0);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
dev = &priv->dev;
test->priv = mock;
dev->mode_config.min_width = MIN_WIDTH;
dev->mode_config.max_width = MAX_WIDTH;
dev->mode_config.min_height = MIN_HEIGHT;
dev->mode_config.max_height = MAX_HEIGHT;
dev->mode_config.funcs = &mock_config_funcs;
test->priv = priv;
return 0;
}
static void drm_test_framebuffer_create(struct kunit *test)
{
const struct drm_framebuffer_test *params = test->param_value;
struct drm_device *mock = test->priv;
int buffer_created = 0;
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *dev = &priv->dev;
mock->dev_private = &buffer_created;
drm_internal_framebuffer_create(mock, &params->cmd, NULL);
KUNIT_EXPECT_EQ(test, params->buffer_created, buffer_created);
priv->buffer_created = false;
drm_internal_framebuffer_create(dev, &params->cmd, NULL);
KUNIT_EXPECT_EQ(test, params->buffer_created, priv->buffer_created);
}
static void drm_framebuffer_test_to_desc(const struct drm_framebuffer_test *t, char *desc)
{
strcpy(desc, t->name);
strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
}
KUNIT_ARRAY_PARAM(drm_framebuffer_create, drm_framebuffer_create_cases,
drm_framebuffer_test_to_desc);
/* Tries to create a framebuffer with modifiers without drm_device supporting it */
static void drm_test_framebuffer_modifiers_not_supported(struct kunit *test)
{
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *dev = &priv->dev;
struct drm_framebuffer *fb;
/* A valid cmd with modifier */
struct drm_mode_fb_cmd2 cmd = {
.width = MAX_WIDTH, .height = MAX_HEIGHT,
.pixel_format = DRM_FORMAT_ABGR8888, .handles = { 1, 0, 0 },
.offsets = { UINT_MAX / 2, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
.flags = DRM_MODE_FB_MODIFIERS,
};
priv->buffer_created = false;
dev->mode_config.fb_modifiers_not_supported = 1;
fb = drm_internal_framebuffer_create(dev, &cmd, NULL);
KUNIT_EXPECT_EQ(test, priv->buffer_created, false);
KUNIT_EXPECT_EQ(test, PTR_ERR(fb), -EINVAL);
}
/* Parameters for testing drm_framebuffer_check_src_coords function */
struct drm_framebuffer_check_src_coords_case {
const char *name;
const int expect;
const unsigned int fb_size;
const uint32_t src_x;
const uint32_t src_y;
/* Deltas to be applied on source */
const uint32_t dsrc_w;
const uint32_t dsrc_h;
};
static const struct drm_framebuffer_check_src_coords_case
drm_framebuffer_check_src_coords_cases[] = {
{ .name = "Success: source fits into fb",
.expect = 0,
},
{ .name = "Fail: overflowing fb with x-axis coordinate",
.expect = -ENOSPC, .src_x = 1, .fb_size = UINT_MAX,
},
{ .name = "Fail: overflowing fb with y-axis coordinate",
.expect = -ENOSPC, .src_y = 1, .fb_size = UINT_MAX,
},
{ .name = "Fail: overflowing fb with source width",
.expect = -ENOSPC, .dsrc_w = 1, .fb_size = UINT_MAX - 1,
},
{ .name = "Fail: overflowing fb with source height",
.expect = -ENOSPC, .dsrc_h = 1, .fb_size = UINT_MAX - 1,
},
};
static void drm_test_framebuffer_check_src_coords(struct kunit *test)
{
const struct drm_framebuffer_check_src_coords_case *params = test->param_value;
const uint32_t src_x = params->src_x;
const uint32_t src_y = params->src_y;
const uint32_t src_w = (params->fb_size << 16) + params->dsrc_w;
const uint32_t src_h = (params->fb_size << 16) + params->dsrc_h;
const struct drm_framebuffer fb = {
.width = params->fb_size,
.height = params->fb_size
};
int ret;
ret = drm_framebuffer_check_src_coords(src_x, src_y, src_w, src_h, &fb);
KUNIT_EXPECT_EQ(test, ret, params->expect);
}
static void
check_src_coords_test_to_desc(const struct drm_framebuffer_check_src_coords_case *t,
char *desc)
{
strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
}
KUNIT_ARRAY_PARAM(check_src_coords, drm_framebuffer_check_src_coords_cases,
check_src_coords_test_to_desc);
/*
* Test if drm_framebuffer_cleanup() really pops out the framebuffer object
* from device's fb_list and decrement the number of framebuffers for that
* device, which is the only things it does.
*/
static void drm_test_framebuffer_cleanup(struct kunit *test)
{
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *dev = &priv->dev;
struct list_head *fb_list = &dev->mode_config.fb_list;
struct drm_format_info format = { };
struct drm_framebuffer fb1 = { .dev = dev, .format = &format };
struct drm_framebuffer fb2 = { .dev = dev, .format = &format };
/* This will result on [fb_list] -> fb2 -> fb1 */
drm_framebuffer_init(dev, &fb1, NULL);
drm_framebuffer_init(dev, &fb2, NULL);
drm_framebuffer_cleanup(&fb1);
/* Now fb2 is the only one element on fb_list */
KUNIT_ASSERT_TRUE(test, list_is_singular(&fb2.head));
KUNIT_ASSERT_EQ(test, dev->mode_config.num_fb, 1);
drm_framebuffer_cleanup(&fb2);
/* Now fb_list is empty */
KUNIT_ASSERT_TRUE(test, list_empty(fb_list));
KUNIT_ASSERT_EQ(test, dev->mode_config.num_fb, 0);
}
/*
* Initialize a framebuffer, lookup its id and test if the returned reference
* matches.
*/
static void drm_test_framebuffer_lookup(struct kunit *test)
{
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *dev = &priv->dev;
struct drm_format_info format = { };
struct drm_framebuffer expected_fb = { .dev = dev, .format = &format };
struct drm_framebuffer *returned_fb;
uint32_t id = 0;
int ret;
ret = drm_framebuffer_init(dev, &expected_fb, NULL);
KUNIT_ASSERT_EQ(test, ret, 0);
id = expected_fb.base.id;
/* Looking for expected_fb */
returned_fb = drm_framebuffer_lookup(dev, NULL, id);
KUNIT_EXPECT_PTR_EQ(test, returned_fb, &expected_fb);
drm_framebuffer_put(returned_fb);
drm_framebuffer_cleanup(&expected_fb);
}
/* Try to lookup an id that is not linked to a framebuffer */
static void drm_test_framebuffer_lookup_inexistent(struct kunit *test)
{
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *dev = &priv->dev;
struct drm_framebuffer *fb;
uint32_t id = 0;
/* Looking for an inexistent framebuffer */
fb = drm_framebuffer_lookup(dev, NULL, id);
KUNIT_EXPECT_NULL(test, fb);
}
/*
* Test if drm_framebuffer_init initializes the framebuffer successfully,
* asserting that its modeset object struct and its refcount are correctly
* set and that strictly one framebuffer is initialized.
*/
static void drm_test_framebuffer_init(struct kunit *test)
{
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *dev = &priv->dev;
struct drm_format_info format = { };
struct drm_framebuffer fb1 = { .dev = dev, .format = &format };
struct drm_framebuffer_funcs funcs = { };
int ret;
ret = drm_framebuffer_init(dev, &fb1, &funcs);
KUNIT_ASSERT_EQ(test, ret, 0);
/* Check if fb->funcs is actually set to the drm_framebuffer_funcs passed on */
KUNIT_EXPECT_PTR_EQ(test, fb1.funcs, &funcs);
/* The fb->comm must be set to the current running process */
KUNIT_EXPECT_STREQ(test, fb1.comm, current->comm);
/* The fb->base must be successfully initialized */
KUNIT_EXPECT_NE(test, fb1.base.id, 0);
KUNIT_EXPECT_EQ(test, fb1.base.type, DRM_MODE_OBJECT_FB);
KUNIT_EXPECT_EQ(test, kref_read(&fb1.base.refcount), 1);
KUNIT_EXPECT_PTR_EQ(test, fb1.base.free_cb, &drm_framebuffer_free);
/* There must be just that one fb initialized */
KUNIT_EXPECT_EQ(test, dev->mode_config.num_fb, 1);
KUNIT_EXPECT_PTR_EQ(test, dev->mode_config.fb_list.prev, &fb1.head);
KUNIT_EXPECT_PTR_EQ(test, dev->mode_config.fb_list.next, &fb1.head);
drm_framebuffer_cleanup(&fb1);
}
/* Try to init a framebuffer without setting its format */
static void drm_test_framebuffer_init_bad_format(struct kunit *test)
{
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *dev = &priv->dev;
struct drm_framebuffer fb1 = { .dev = dev, .format = NULL };
struct drm_framebuffer_funcs funcs = { };
int ret;
/* Fails if fb.format isn't set */
ret = drm_framebuffer_init(dev, &fb1, &funcs);
KUNIT_EXPECT_EQ(test, ret, -EINVAL);
}
/*
* Test calling drm_framebuffer_init() passing a framebuffer linked to a
* different drm_device parent from the one passed on the first argument, which
* must fail.
*/
static void drm_test_framebuffer_init_dev_mismatch(struct kunit *test)
{
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *right_dev = &priv->dev;
struct drm_device *wrong_dev;
struct device *wrong_dev_parent;
struct drm_format_info format = { };
struct drm_framebuffer fb1 = { .dev = right_dev, .format = &format };
struct drm_framebuffer_funcs funcs = { };
int ret;
wrong_dev_parent = kunit_device_register(test, "drm-kunit-wrong-device-mock");
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wrong_dev_parent);
wrong_dev = __drm_kunit_helper_alloc_drm_device(test, wrong_dev_parent,
sizeof(struct drm_device),
0, 0);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wrong_dev);
/* Fails if fb->dev doesn't point to the drm_device passed on first arg */
ret = drm_framebuffer_init(wrong_dev, &fb1, &funcs);
KUNIT_EXPECT_EQ(test, ret, -EINVAL);
}
static void destroy_free_mock(struct drm_framebuffer *fb)
{
struct drm_framebuffer_test_priv *priv = container_of(fb->dev, typeof(*priv), dev);
priv->buffer_freed = true;
}
static struct drm_framebuffer_funcs framebuffer_funcs_free_mock = {
.destroy = destroy_free_mock,
};
/*
* In summary, the drm_framebuffer_free() function must implicitly call
* fb->funcs->destroy() and garantee that the framebufer object is unregistered
* from the drm_device idr pool.
*/
static void drm_test_framebuffer_free(struct kunit *test)
{
struct drm_framebuffer_test_priv *priv = test->priv;
struct drm_device *dev = &priv->dev;
struct drm_mode_object *obj;
struct drm_framebuffer fb = {
.dev = dev,
.funcs = &framebuffer_funcs_free_mock,
};
int id, ret;
priv->buffer_freed = false;
/*
* Mock a framebuffer that was not unregistered at the moment of the
* drm_framebuffer_free() call.
*/
ret = drm_mode_object_add(dev, &fb.base, DRM_MODE_OBJECT_FB);
KUNIT_ASSERT_EQ(test, ret, 0);
id = fb.base.id;
drm_framebuffer_free(&fb.base.refcount);
/* The framebuffer object must be unregistered */
obj = drm_mode_object_find(dev, NULL, id, DRM_MODE_OBJECT_FB);
KUNIT_EXPECT_PTR_EQ(test, obj, NULL);
KUNIT_EXPECT_EQ(test, fb.base.id, 0);
/* Test if fb->funcs->destroy() was called */
KUNIT_EXPECT_EQ(test, priv->buffer_freed, true);
}
static struct kunit_case drm_framebuffer_tests[] = {
KUNIT_CASE_PARAM(drm_test_framebuffer_check_src_coords, check_src_coords_gen_params),
KUNIT_CASE(drm_test_framebuffer_cleanup),
KUNIT_CASE_PARAM(drm_test_framebuffer_create, drm_framebuffer_create_gen_params),
KUNIT_CASE(drm_test_framebuffer_free),
KUNIT_CASE(drm_test_framebuffer_init),
KUNIT_CASE(drm_test_framebuffer_init_bad_format),
KUNIT_CASE(drm_test_framebuffer_init_dev_mismatch),
KUNIT_CASE(drm_test_framebuffer_lookup),
KUNIT_CASE(drm_test_framebuffer_lookup_inexistent),
KUNIT_CASE(drm_test_framebuffer_modifiers_not_supported),
{ }
};

Some files were not shown because too many files have changed in this diff Show More