mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-15 01:24:33 +00:00
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:
commit
7fefa1edc2
@ -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
|
@ -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 {
|
||||
|
@ -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"
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
----------------
|
||||
|
@ -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
|
||||
===========
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
87
drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c
Normal file
87
drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c
Normal 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");
|
@ -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),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {}
|
||||
|
111
drivers/gpu/drm/bridge/ti-tdp158.c
Normal file
111
drivers/gpu/drm/bridge/ti-tdp158.c
Normal 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");
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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[] = {
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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]);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 = {
|
||||
|
@ -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:
|
||||
|
@ -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, ¶m->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,
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 */
|
||||
|
@ -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, ×tamp_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,
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 |
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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, ¶ms->cmd, NULL);
|
||||
KUNIT_EXPECT_EQ(test, params->buffer_created, buffer_created);
|
||||
priv->buffer_created = false;
|
||||
drm_internal_framebuffer_create(dev, ¶ms->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
Loading…
x
Reference in New Issue
Block a user