Commit d076e2bd authored by Daniel Vetter's avatar Daniel Vetter

Merge tag 'drm-misc-next-2024-07-04' of...

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

drm-misc-next for $kernel-version:

UAPI Changes:

Cross-subsystem Changes:

Core Changes:
  - dp/mst: Fix daisy-chaining at resume
  - dsc: Add helper to dump the DSC configuration
  - tests: Add tests for the new monochrome TV mode variant

Driver Changes:
  - ast: Refactor the mode setting code
  - panfrost: Fix devfreq job reporting
  - stm: Add LDVS support, DSI PHY updates
  - panels:
    - New panel: AUO G104STN01, K&d kd101ne3-40ti,
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
From: Maxime Ripard <mripard@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240704-curvy-outstanding-lizard-bcea78@houat
parents bfc10936 896868ed
......@@ -17,6 +17,7 @@ properties:
items:
- enum:
- chongzhou,cz101b4001
- kingdisplay,kd101ne3-40ti
- radxa,display-10hd-ad001
- radxa,display-8hd-ad002
- const: jadard,jd9365da-h3
......
......@@ -51,6 +51,8 @@ properties:
- auo,g101evn010
# AU Optronics Corporation 10.4" (800x600) color TFT LCD panel
- auo,g104sn02
# AU Optronics Corporation 10.4" (800x600) color TFT LCD panel
- auo,g104stn01
# AU Optronics Corporation 12.1" (1280x800) TFT LCD panel
- auo,g121ean01
# AU Optronics Corporation 15.6" (1366x768) TFT LCD panel
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/st,stm32mp25-lvds.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: STMicroelectronics STM32 LVDS Display Interface Transmitter
maintainers:
- Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
- Yannick Fertre <yannick.fertre@foss.st.com>
description: |
The STMicroelectronics STM32 LVDS Display Interface Transmitter handles the
LVDS protocol: it maps the pixels received from the upstream Pixel-DMA (LTDC)
onto the LVDS PHY.
It is composed of three sub blocks:
- LVDS host: handles the LVDS protocol (FPD / OpenLDI) and maps its input
pixels onto the data lanes of the PHY
- LVDS PHY: parallelize the data and drives the LVDS data lanes
- LVDS wrapper: handles top-level settings
The LVDS controller driver supports the following high-level features:
- FDP-Link-I and OpenLDI (v0.95) protocols
- Single-Link or Dual-Link operation
- Single-Display or Double-Display (with the same content duplicated on both)
- Flexible Bit-Mapping, including JEIDA and VESA
- RGB888 or RGB666 output
- Synchronous design, with one input pixel per clock cycle
properties:
compatible:
const: st,stm32mp25-lvds
"#clock-cells":
const: 0
description:
Provides the internal LVDS PHY clock to the framework.
reg:
maxItems: 1
clocks:
items:
- description: APB peripheral clock
- description: Reference clock for the internal PLL
clock-names:
items:
- const: pclk
- const: ref
resets:
maxItems: 1
ports:
$ref: /schemas/graph.yaml#/properties/ports
properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description:
LVDS input port node, connected to the LTDC RGB output port.
port@1:
$ref: /schemas/graph.yaml#/properties/port
description:
LVDS output port node, connected to a panel or bridge input port.
required:
- port@0
- port@1
required:
- compatible
- "#clock-cells"
- reg
- clocks
- clock-names
- resets
- ports
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/st,stm32mp25-rcc.h>
#include <dt-bindings/reset/st,stm32mp25-rcc.h>
lvds: lvds@48060000 {
compatible = "st,stm32mp25-lvds";
reg = <0x48060000 0x2000>;
#clock-cells = <0>;
clocks = <&rcc CK_BUS_LVDS>, <&rcc CK_KER_LVDSPHY>;
clock-names = "pclk", "ref";
resets = <&rcc LVDS_R>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
lvds_in: endpoint {
remote-endpoint = <&ltdc_ep1_out>;
};
};
port@1 {
reg = <1>;
lvds_out0: endpoint {
remote-endpoint = <&lvds_panel_in>;
};
};
};
};
...
......@@ -7484,6 +7484,7 @@ L: dri-devel@lists.freedesktop.org
S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/st,stm32-ltdc.yaml
F: Documentation/devicetree/bindings/display/st,stm32mp25-lvds.yaml
F: drivers/gpu/drm/stm
DRM DRIVERS FOR TI KEYSTONE
......
......@@ -303,7 +303,7 @@ static void ast_set_std_reg(struct ast_device *ast,
/* Set SEQ; except Screen Disable field */
ast_set_index_reg(ast, AST_IO_VGASRI, 0x00, 0x03);
ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x01, 0xdf, stdtable->seq[0]);
ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x01, 0x20, stdtable->seq[0]);
for (i = 1; i < 4; i++) {
jreg = stdtable->seq[i];
ast_set_index_reg(ast, AST_IO_VGASRI, (i + 1), jreg);
......@@ -649,12 +649,12 @@ static void ast_primary_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_framebuffer *old_fb = old_plane_state->fb;
struct ast_plane *ast_plane = to_ast_plane(plane);
struct drm_crtc *crtc = plane_state->crtc;
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
struct drm_rect damage;
struct drm_atomic_helper_damage_iter iter;
if (!old_fb || (fb->format != old_fb->format)) {
struct drm_crtc *crtc = plane_state->crtc;
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
if (!old_fb || (fb->format != old_fb->format) || crtc_state->mode_changed) {
struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state);
struct ast_vbios_mode_info *vbios_mode_info = &ast_crtc_state->vbios_mode_info;
......@@ -690,15 +690,15 @@ static void ast_primary_plane_helper_atomic_enable(struct drm_plane *plane,
* Therefore only reprogram the address after enabling the plane.
*/
ast_set_start_address_crt1(ast, (u32)ast_plane->offset);
ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x1, 0xdf, 0x00);
}
static void ast_primary_plane_helper_atomic_disable(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct ast_device *ast = to_ast_device(plane->dev);
ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x1, 0xdf, 0x20);
/*
* Keep this empty function to avoid calling
* atomic_update when disabling the plane.
*/
}
static int ast_primary_plane_helper_get_scanout_buffer(struct drm_plane *plane,
......@@ -1019,62 +1019,6 @@ static int ast_cursor_plane_init(struct ast_device *ast)
* CRTC
*/
static void ast_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct ast_device *ast = to_ast_device(crtc->dev);
u8 ch = AST_DPMS_VSYNC_OFF | AST_DPMS_HSYNC_OFF;
struct ast_crtc_state *ast_state;
const struct drm_format_info *format;
struct ast_vbios_mode_info *vbios_mode_info;
/* TODO: Maybe control display signal generation with
* Sync Enable (bit CR17.7).
*/
switch (mode) {
case DRM_MODE_DPMS_ON:
ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x01, 0xdf, 0);
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0xfc, 0);
if (ast->tx_chip_types & AST_TX_DP501_BIT)
ast_set_dp501_video_output(crtc->dev, 1);
if (ast->tx_chip_types & AST_TX_ASTDP_BIT) {
ast_dp_power_on_off(crtc->dev, AST_DP_POWER_ON);
ast_wait_for_vretrace(ast);
ast_dp_set_on_off(crtc->dev, 1);
}
ast_state = to_ast_crtc_state(crtc->state);
format = ast_state->format;
if (format) {
vbios_mode_info = &ast_state->vbios_mode_info;
ast_set_color_reg(ast, format);
ast_set_vbios_color_reg(ast, format, vbios_mode_info);
if (crtc->state->gamma_lut)
ast_crtc_set_gamma(ast, format, crtc->state->gamma_lut->data);
else
ast_crtc_set_gamma_linear(ast, format);
}
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
ch = mode;
if (ast->tx_chip_types & AST_TX_DP501_BIT)
ast_set_dp501_video_output(crtc->dev, 0);
if (ast->tx_chip_types & AST_TX_ASTDP_BIT) {
ast_dp_set_on_off(crtc->dev, 0);
ast_dp_power_on_off(crtc->dev, AST_DP_POWER_OFF);
}
ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x01, 0xdf, 0x20);
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0xfc, ch);
break;
}
}
static enum drm_mode_status
ast_crtc_helper_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)
{
......@@ -1147,6 +1091,33 @@ ast_crtc_helper_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode
return status;
}
static void ast_crtc_helper_mode_set_nofb(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct ast_device *ast = to_ast_device(dev);
struct drm_crtc_state *crtc_state = crtc->state;
struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state);
struct ast_vbios_mode_info *vbios_mode_info =
&ast_crtc_state->vbios_mode_info;
struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
/*
* Ensure that no scanout takes place before reprogramming mode
* and format registers.
*
* TODO: Get vblank interrupts working and remove this line.
*/
ast_wait_for_vretrace(ast);
ast_set_vbios_mode_reg(ast, adjusted_mode, vbios_mode_info);
ast_set_index_reg(ast, AST_IO_VGACRI, 0xa1, 0x06);
ast_set_std_reg(ast, adjusted_mode, vbios_mode_info);
ast_set_crtc_reg(ast, adjusted_mode, vbios_mode_info);
ast_set_dclk_reg(ast, adjusted_mode, vbios_mode_info);
ast_set_crtthd_reg(ast);
ast_set_sync_reg(ast, adjusted_mode, vbios_mode_info);
}
static int ast_crtc_helper_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
......@@ -1206,7 +1177,6 @@ ast_crtc_helper_atomic_flush(struct drm_crtc *crtc,
struct drm_device *dev = crtc->dev;
struct ast_device *ast = to_ast_device(dev);
struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state);
struct ast_vbios_mode_info *vbios_mode_info = &ast_crtc_state->vbios_mode_info;
/*
* The gamma LUT has to be reloaded after changing the primary
......@@ -1220,40 +1190,27 @@ ast_crtc_helper_atomic_flush(struct drm_crtc *crtc,
else
ast_crtc_set_gamma_linear(ast, ast_crtc_state->format);
}
//Set Aspeed Display-Port
if (ast->tx_chip_types & AST_TX_ASTDP_BIT)
ast_dp_set_mode(crtc, vbios_mode_info);
}
static void ast_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state)
{
struct drm_device *dev = crtc->dev;
struct ast_device *ast = to_ast_device(dev);
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state);
struct ast_vbios_mode_info *vbios_mode_info =
&ast_crtc_state->vbios_mode_info;
struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
ast_set_vbios_mode_reg(ast, adjusted_mode, vbios_mode_info);
ast_set_index_reg(ast, AST_IO_VGACRI, 0xa1, 0x06);
ast_set_std_reg(ast, adjusted_mode, vbios_mode_info);
ast_set_crtc_reg(ast, adjusted_mode, vbios_mode_info);
ast_set_dclk_reg(ast, adjusted_mode, vbios_mode_info);
ast_set_crtthd_reg(ast);
ast_set_sync_reg(ast, adjusted_mode, vbios_mode_info);
struct ast_device *ast = to_ast_device(crtc->dev);
ast_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0xfc, 0x00);
ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x01, 0xdf, 0x00);
}
static void ast_crtc_helper_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *state)
{
struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
struct drm_device *dev = crtc->dev;
struct ast_device *ast = to_ast_device(dev);
struct ast_device *ast = to_ast_device(crtc->dev);
u8 vgacrb6;
ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x01, 0xdf, AST_IO_VGASR1_SD);
ast_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
vgacrb6 = AST_IO_VGACRB6_VSYNC_OFF |
AST_IO_VGACRB6_HSYNC_OFF;
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0xfc, vgacrb6);
/*
* HW cursors require the underlying primary plane and CRTC to
......@@ -1266,16 +1223,11 @@ static void ast_crtc_helper_atomic_disable(struct drm_crtc *crtc, struct drm_ato
* simple pageflips on the planes.
*/
drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);
/*
* Ensure that no scanout takes place before reprogramming mode
* and format registers.
*/
ast_wait_for_vretrace(ast);
}
static const struct drm_crtc_helper_funcs ast_crtc_helper_funcs = {
.mode_valid = ast_crtc_helper_mode_valid,
.mode_set_nofb = ast_crtc_helper_mode_set_nofb,
.atomic_check = ast_crtc_helper_atomic_check,
.atomic_flush = ast_crtc_helper_atomic_flush,
.atomic_enable = ast_crtc_helper_atomic_enable,
......@@ -1521,6 +1473,27 @@ static const struct drm_encoder_funcs ast_dp501_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static void ast_dp501_encoder_helper_atomic_enable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct drm_device *dev = encoder->dev;
ast_set_dp501_video_output(dev, 1);
}
static void ast_dp501_encoder_helper_atomic_disable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct drm_device *dev = encoder->dev;
ast_set_dp501_video_output(dev, 0);
}
static const struct drm_encoder_helper_funcs ast_dp501_encoder_helper_funcs = {
.atomic_enable = ast_dp501_encoder_helper_atomic_enable,
.atomic_disable = ast_dp501_encoder_helper_atomic_disable,
};
/*
* DP501 Connector
*/
......@@ -1607,6 +1580,8 @@ static int ast_dp501_output_init(struct ast_device *ast)
DRM_MODE_ENCODER_TMDS, NULL);
if (ret)
return ret;
drm_encoder_helper_add(encoder, &ast_dp501_encoder_helper_funcs);
encoder->possible_crtcs = drm_crtc_mask(crtc);
ret = ast_dp501_connector_init(dev, connector);
......@@ -1628,6 +1603,43 @@ static const struct drm_encoder_funcs ast_astdp_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static void ast_astdp_encoder_helper_atomic_mode_set(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct drm_crtc *crtc = crtc_state->crtc;
struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state);
struct ast_vbios_mode_info *vbios_mode_info = &ast_crtc_state->vbios_mode_info;
ast_dp_set_mode(crtc, vbios_mode_info);
}
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);
ast_dp_power_on_off(dev, AST_DP_POWER_ON);
ast_wait_for_vretrace(ast);
ast_dp_set_on_off(dev, 1);
}
static void ast_astdp_encoder_helper_atomic_disable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct drm_device *dev = encoder->dev;
ast_dp_set_on_off(dev, 0);
ast_dp_power_on_off(dev, AST_DP_POWER_OFF);
}
static const struct drm_encoder_helper_funcs ast_astdp_encoder_helper_funcs = {
.atomic_mode_set = ast_astdp_encoder_helper_atomic_mode_set,
.atomic_enable = ast_astdp_encoder_helper_atomic_enable,
.atomic_disable = ast_astdp_encoder_helper_atomic_disable,
};
/*
* ASPEED Display-Port Connector
*/
......@@ -1726,6 +1738,8 @@ static int ast_astdp_output_init(struct ast_device *ast)
DRM_MODE_ENCODER_TMDS, NULL);
if (ret)
return ret;
drm_encoder_helper_add(encoder, &ast_astdp_encoder_helper_funcs);
encoder->possible_crtcs = drm_crtc_mask(crtc);
ret = ast_astdp_connector_init(dev, connector);
......@@ -1851,7 +1865,7 @@ static void ast_mode_config_helper_atomic_commit_tail(struct drm_atomic_state *s
* the I/O-register lock. Released in atomic_flush().
*/
mutex_lock(&ast->modeset_lock);
drm_atomic_helper_commit_tail_rpm(state);
drm_atomic_helper_commit_tail(state);
mutex_unlock(&ast->modeset_lock);
}
......
......@@ -22,6 +22,7 @@
#define AST_IO_VGAER_VGA_ENABLE BIT(0)
#define AST_IO_VGASRI (0x44)
#define AST_IO_VGASR1_SD BIT(5)
#define AST_IO_VGADRR (0x47)
#define AST_IO_VGADWR (0x48)
#define AST_IO_VGAPDR (0x49)
......@@ -31,6 +32,8 @@
#define AST_IO_VGACR80_PASSWORD (0xa8)
#define AST_IO_VGACRA1_VGAIO_DISABLED BIT(1)
#define AST_IO_VGACRA1_MMIO_ENABLED BIT(2)
#define AST_IO_VGACRB6_HSYNC_OFF BIT(0)
#define AST_IO_VGACRB6_VSYNC_OFF BIT(1)
#define AST_IO_VGACRCB_HWC_16BPP BIT(0) /* set: ARGB4444, cleared: 2bpp palette */
#define AST_IO_VGACRCB_HWC_ENABLED BIT(1)
......@@ -75,13 +78,6 @@
#define ASTDP_HOST_EDID_READ_DONE BIT(0)
#define ASTDP_HOST_EDID_READ_DONE_MASK GENMASK(0, 0)
/*
* CRB8[b1]: Enable VSYNC off
* CRB8[b0]: Enable HSYNC off
*/
#define AST_DPMS_VSYNC_OFF BIT(1)
#define AST_DPMS_HSYNC_OFF BIT(0)
/*
* CRDF[b4]: Mirror of AST_DP_VIDEO_ENABLE
* Precondition: A. ~AST_DP_PHY_SLEEP &&
......
......@@ -35,6 +35,7 @@
#include <drm/display/drm_dp_helper.h>
#include <drm/display/drm_dp_mst_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_fixed.h>
#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include <drm/drm_panel.h>
......@@ -4151,9 +4152,9 @@ int drm_dp_bw_overhead(int lane_count, int hactive,
int symbol_cycles;
if (lane_count == 0 || hactive == 0 || bpp_x16 == 0) {
DRM_DEBUG_KMS("Invalid BW overhead params: lane_count %d, hactive %d, bpp_x16 %d.%04d\n",
DRM_DEBUG_KMS("Invalid BW overhead params: lane_count %d, hactive %d, bpp_x16 " FXP_Q4_FMT "\n",
lane_count, hactive,
bpp_x16 >> 4, (bpp_x16 & 0xf) * 625);
FXP_Q4_ARGS(bpp_x16));
return 0;
}
......
......@@ -2929,7 +2929,7 @@ static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
/* FIXME: Actually do some real error handling here */
ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
if (ret <= 0) {
if (ret < 0) {
drm_err(mgr->dev, "Sending link address failed with %d\n", ret);
goto out;
}
......@@ -2981,7 +2981,7 @@ static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
mutex_unlock(&mgr->lock);
out:
if (ret <= 0)
if (ret < 0)
mstb->link_address_sent = false;
kfree(txmsg);
return ret < 0 ? ret : changed;
......@@ -4069,6 +4069,7 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
if (up_req->msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
const struct drm_dp_connection_status_notify *conn_stat =
&up_req->msg.u.conn_stat;
bool handle_csn;
drm_dbg_kms(mgr->dev, "Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n",
conn_stat->port_number,
......@@ -4077,6 +4078,16 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
conn_stat->message_capability_status,
conn_stat->input_port,
conn_stat->peer_device_type);
mutex_lock(&mgr->probe_lock);
handle_csn = mgr->mst_primary->link_address_sent;
mutex_unlock(&mgr->probe_lock);
if (!handle_csn) {
drm_dbg_kms(mgr->dev, "Got CSN before finish topology probing. Skip it.");
kfree(up_req);
goto out;
}
} else if (up_req->msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
const struct drm_dp_resource_status_notify *res_stat =
&up_req->msg.u.resource_stat;
......
......@@ -14,6 +14,7 @@
#include <drm/display/drm_dp_helper.h>
#include <drm/display/drm_dsc_helper.h>
#include <drm/drm_fixed.h>
#include <drm/drm_print.h>
/**
......@@ -1472,3 +1473,93 @@ u32 drm_dsc_flatness_det_thresh(const struct drm_dsc_config *dsc)
return 2 << (dsc->bits_per_component - 8);
}
EXPORT_SYMBOL(drm_dsc_flatness_det_thresh);
static void drm_dsc_dump_config_main_params(struct drm_printer *p, int indent,
const struct drm_dsc_config *cfg)
{
drm_printf_indent(p, indent,
"dsc-cfg: version: %d.%d, picture: w=%d, h=%d, slice: count=%d, w=%d, h=%d, size=%d\n",
cfg->dsc_version_major, cfg->dsc_version_minor,
cfg->pic_width, cfg->pic_height,
cfg->slice_count, cfg->slice_width, cfg->slice_height, cfg->slice_chunk_size);
drm_printf_indent(p, indent,
"dsc-cfg: mode: block-pred=%s, vbr=%s, rgb=%s, simple-422=%s, native-422=%s, native-420=%s\n",
str_yes_no(cfg->block_pred_enable), str_yes_no(cfg->vbr_enable),
str_yes_no(cfg->convert_rgb),
str_yes_no(cfg->simple_422), str_yes_no(cfg->native_422), str_yes_no(cfg->native_420));
drm_printf_indent(p, indent,
"dsc-cfg: color-depth: uncompressed-bpc=%d, compressed-bpp=" FXP_Q4_FMT " line-buf-bpp=%d\n",
cfg->bits_per_component, FXP_Q4_ARGS(cfg->bits_per_pixel), cfg->line_buf_depth);
drm_printf_indent(p, indent,
"dsc-cfg: rc-model: size=%d, bits=%d, mux-word-size: %d, initial-delays: xmit=%d, dec=%d\n",
cfg->rc_model_size, cfg->rc_bits, cfg->mux_word_size,
cfg->initial_xmit_delay, cfg->initial_dec_delay);
drm_printf_indent(p, indent,
"dsc-cfg: offsets: initial=%d, final=%d, slice-bpg=%d\n",
cfg->initial_offset, cfg->final_offset, cfg->slice_bpg_offset);
drm_printf_indent(p, indent,
"dsc-cfg: line-bpg-offsets: first=%d, non-first=%d, second=%d, non-second=%d, second-adj=%d\n",
cfg->first_line_bpg_offset, cfg->nfl_bpg_offset,
cfg->second_line_bpg_offset, cfg->nsl_bpg_offset, cfg->second_line_offset_adj);
drm_printf_indent(p, indent,
"dsc-cfg: rc-tgt-offsets: low=%d, high=%d, rc-edge-factor: %d, rc-quant-incr-limits: [0]=%d, [1]=%d\n",
cfg->rc_tgt_offset_low, cfg->rc_tgt_offset_high,
cfg->rc_edge_factor, cfg->rc_quant_incr_limit0, cfg->rc_quant_incr_limit1);
drm_printf_indent(p, indent,
"dsc-cfg: initial-scale: %d, scale-intervals: increment=%d, decrement=%d\n",
cfg->initial_scale_value, cfg->scale_increment_interval, cfg->scale_decrement_interval);
drm_printf_indent(p, indent,
"dsc-cfg: flatness: min-qp=%d, max-qp=%d\n",
cfg->flatness_min_qp, cfg->flatness_max_qp);
}
static void drm_dsc_dump_config_rc_params(struct drm_printer *p, int indent,
const struct drm_dsc_config *cfg)
{
const u16 *bt = cfg->rc_buf_thresh;
const struct drm_dsc_rc_range_parameters *rp = cfg->rc_range_params;
BUILD_BUG_ON(ARRAY_SIZE(cfg->rc_buf_thresh) != 14);
BUILD_BUG_ON(ARRAY_SIZE(cfg->rc_range_params) != 15);
drm_printf_indent(p, indent,
"dsc-cfg: rc-level: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14\n");
drm_printf_indent(p, indent,
"dsc-cfg: rc-buf-thresh: %3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d\n",
bt[0], bt[1], bt[2], bt[3], bt[4], bt[5], bt[6], bt[7],
bt[8], bt[9], bt[10], bt[11], bt[12], bt[13]);
drm_printf_indent(p, indent,
"dsc-cfg: rc-min-qp: %3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d\n",
rp[0].range_min_qp, rp[1].range_min_qp, rp[2].range_min_qp, rp[3].range_min_qp,
rp[4].range_min_qp, rp[5].range_min_qp, rp[6].range_min_qp, rp[7].range_min_qp,
rp[8].range_min_qp, rp[9].range_min_qp, rp[10].range_min_qp, rp[11].range_min_qp,
rp[12].range_min_qp, rp[13].range_min_qp, rp[14].range_min_qp);
drm_printf_indent(p, indent,
"dsc-cfg: rc-max-qp: %3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d\n",
rp[0].range_max_qp, rp[1].range_max_qp, rp[2].range_max_qp, rp[3].range_max_qp,
rp[4].range_max_qp, rp[5].range_max_qp, rp[6].range_max_qp, rp[7].range_max_qp,
rp[8].range_max_qp, rp[9].range_max_qp, rp[10].range_max_qp, rp[11].range_max_qp,
rp[12].range_max_qp, rp[13].range_max_qp, rp[14].range_max_qp);
drm_printf_indent(p, indent,
"dsc-cfg: rc-bpg-offset: %3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d\n",
rp[0].range_bpg_offset, rp[1].range_bpg_offset, rp[2].range_bpg_offset, rp[3].range_bpg_offset,
rp[4].range_bpg_offset, rp[5].range_bpg_offset, rp[6].range_bpg_offset, rp[7].range_bpg_offset,
rp[8].range_bpg_offset, rp[9].range_bpg_offset, rp[10].range_bpg_offset, rp[11].range_bpg_offset,
rp[12].range_bpg_offset, rp[13].range_bpg_offset, rp[14].range_bpg_offset);
}
/**
* drm_dsc_dump_config - Dump the provided DSC configuration
* @p: The printer used for output
* @indent: Tab indentation level (max 5)
* @cfg: DSC configuration to print
*
* Print the provided DSC configuration in @cfg.
*/
void drm_dsc_dump_config(struct drm_printer *p, int indent,
const struct drm_dsc_config *cfg)
{
drm_dsc_dump_config_main_params(p, indent, cfg);
drm_dsc_dump_config_rc_params(p, indent, cfg);
}
EXPORT_SYMBOL(drm_dsc_dump_config);
......@@ -197,7 +197,7 @@ void drmm_release_action(struct drm_device *dev,
spin_lock_irqsave(&dev->managed.lock, flags);
list_for_each_entry_reverse(dr, &dev->managed.resources, node.entry) {
if (dr->node.release == action) {
if (!data || (data && *(void **)dr->data == data)) {
if (!data || *(void **)dr->data == data) {
dr_match = dr;
del_dr(dev, dr_match);
break;
......
......@@ -501,3 +501,4 @@ module_platform_driver(lima_platform_driver);
MODULE_AUTHOR("Lima Project Developers");
MODULE_DESCRIPTION("Lima DRM Driver");
MODULE_LICENSE("GPL v2");
MODULE_SOFTDEP("pre: governor_simpleondemand");
......@@ -111,12 +111,12 @@ static inline void mga_wait_vsync(struct mga_device *mdev)
unsigned int status = 0;
do {
status = RREG32(MGAREG_Status);
status = RREG32(MGAREG_STATUS);
} while ((status & 0x08) && time_before(jiffies, timeout));
timeout = jiffies + HZ/10;
status = 0;
do {
status = RREG32(MGAREG_Status);
status = RREG32(MGAREG_STATUS);
} while (!(status & 0x08) && time_before(jiffies, timeout));
}
......@@ -125,7 +125,7 @@ static inline void mga_wait_busy(struct mga_device *mdev)
unsigned long timeout = jiffies + HZ;
unsigned int status = 0;
do {
status = RREG8(MGAREG_Status + 2);
status = RREG8(MGAREG_STATUS + 2);
} while ((status & 0x01) && time_before(jiffies, timeout));
}
......
......@@ -102,7 +102,7 @@
#define MGAREG_EXEC 0x0100
#define MGAREG_FIFOSTATUS 0x1e10
#define MGAREG_Status 0x1e14
#define MGAREG_STATUS 0x1e14
#define MGAREG_CACHEFLUSH 0x1fff
#define MGAREG_ICLEAR 0x1e18
#define MGAREG_IEN 0x1e1c
......
......@@ -19,25 +19,30 @@
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#define JD9365DA_INIT_CMD_LEN 2
struct jadard_init_cmd {
u8 data[JD9365DA_INIT_CMD_LEN];
};
struct jadard;
struct jadard_panel_desc {
const struct drm_display_mode mode;
unsigned int lanes;
enum mipi_dsi_pixel_format format;
const struct jadard_init_cmd *init_cmds;
int (*init)(struct jadard *jadard);
u32 num_init_cmds;
bool lp11_before_reset;
bool reset_before_power_off_vcioo;
unsigned int vcioo_to_lp11_delay_ms;
unsigned int lp11_to_reset_delay_ms;
unsigned int exit_sleep_to_display_on_delay_ms;
unsigned int display_on_delay_ms;
unsigned int backlight_off_to_display_off_delay_ms;
unsigned int display_off_to_enter_sleep_delay_ms;
unsigned int enter_sleep_to_reset_down_delay_ms;
};
struct jadard {
struct drm_panel panel;
struct mipi_dsi_device *dsi;
const struct jadard_panel_desc *desc;
enum drm_panel_orientation orientation;
struct regulator *vdd;
struct regulator *vccio;
struct gpio_desc *reset;
......@@ -50,51 +55,43 @@ static inline struct jadard *panel_to_jadard(struct drm_panel *panel)
static int jadard_enable(struct drm_panel *panel)
{
struct device *dev = panel->dev;
struct jadard *jadard = panel_to_jadard(panel);
const struct jadard_panel_desc *desc = jadard->desc;
struct mipi_dsi_device *dsi = jadard->dsi;
unsigned int i;
int err;
struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi };
msleep(10);
for (i = 0; i < desc->num_init_cmds; i++) {
const struct jadard_init_cmd *cmd = &desc->init_cmds[i];
msleep(120);
err = mipi_dsi_dcs_write_buffer(dsi, cmd->data, JD9365DA_INIT_CMD_LEN);
if (err < 0)
return err;
}
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
msleep(120);
if (jadard->desc->exit_sleep_to_display_on_delay_ms)
mipi_dsi_msleep(&dsi_ctx, jadard->desc->exit_sleep_to_display_on_delay_ms);
err = mipi_dsi_dcs_exit_sleep_mode(dsi);
if (err < 0)
DRM_DEV_ERROR(dev, "failed to exit sleep mode ret = %d\n", err);
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
err = mipi_dsi_dcs_set_display_on(dsi);
if (err < 0)
DRM_DEV_ERROR(dev, "failed to set display on ret = %d\n", err);
if (jadard->desc->display_on_delay_ms)
mipi_dsi_msleep(&dsi_ctx, jadard->desc->display_on_delay_ms);
return 0;
return dsi_ctx.accum_err;
}
static int jadard_disable(struct drm_panel *panel)
{
struct device *dev = panel->dev;
struct jadard *jadard = panel_to_jadard(panel);
int ret;
struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi };
ret = mipi_dsi_dcs_set_display_off(jadard->dsi);
if (ret < 0)
DRM_DEV_ERROR(dev, "failed to set display off: %d\n", ret);
if (jadard->desc->backlight_off_to_display_off_delay_ms)
mipi_dsi_msleep(&dsi_ctx, jadard->desc->backlight_off_to_display_off_delay_ms);
ret = mipi_dsi_dcs_enter_sleep_mode(jadard->dsi);
if (ret < 0)
DRM_DEV_ERROR(dev, "failed to enter sleep mode: %d\n", ret);
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
return 0;
if (jadard->desc->display_off_to_enter_sleep_delay_ms)
mipi_dsi_msleep(&dsi_ctx, jadard->desc->display_off_to_enter_sleep_delay_ms);
mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
if (jadard->desc->enter_sleep_to_reset_down_delay_ms)
mipi_dsi_msleep(&dsi_ctx, jadard->desc->enter_sleep_to_reset_down_delay_ms);
return dsi_ctx.accum_err;
}
static int jadard_prepare(struct drm_panel *panel)
......@@ -110,6 +107,18 @@ static int jadard_prepare(struct drm_panel *panel)
if (ret)
return ret;
if (jadard->desc->vcioo_to_lp11_delay_ms)
msleep(jadard->desc->vcioo_to_lp11_delay_ms);
if (jadard->desc->lp11_before_reset) {
ret = mipi_dsi_dcs_nop(jadard->dsi);
if (ret)
return ret;
}
if (jadard->desc->lp11_to_reset_delay_ms)
msleep(jadard->desc->lp11_to_reset_delay_ms);
gpiod_set_value(jadard->reset, 1);
msleep(5);
......@@ -117,7 +126,11 @@ static int jadard_prepare(struct drm_panel *panel)
msleep(10);
gpiod_set_value(jadard->reset, 1);
msleep(120);
msleep(130);
ret = jadard->desc->init(jadard);
if (ret)
return ret;
return 0;
}
......@@ -129,6 +142,12 @@ static int jadard_unprepare(struct drm_panel *panel)
gpiod_set_value(jadard->reset, 1);
msleep(120);
if (jadard->desc->reset_before_power_off_vcioo) {
gpiod_set_value(jadard->reset, 0);
usleep_range(1000, 2000);
}
regulator_disable(jadard->vdd);
regulator_disable(jadard->vccio);
......@@ -159,184 +178,197 @@ static int jadard_get_modes(struct drm_panel *panel,
return 1;
}
static enum drm_panel_orientation jadard_panel_get_orientation(struct drm_panel *panel)
{
struct jadard *jadard = panel_to_jadard(panel);
return jadard->orientation;
}
static const struct drm_panel_funcs jadard_funcs = {
.disable = jadard_disable,
.unprepare = jadard_unprepare,
.prepare = jadard_prepare,
.enable = jadard_enable,
.get_modes = jadard_get_modes,
.get_orientation = jadard_panel_get_orientation,
};
static const struct jadard_init_cmd radxa_display_8hd_ad002_init_cmds[] = {
{ .data = { 0xE0, 0x00 } },
{ .data = { 0xE1, 0x93 } },
{ .data = { 0xE2, 0x65 } },
{ .data = { 0xE3, 0xF8 } },
{ .data = { 0x80, 0x03 } },
{ .data = { 0xE0, 0x01 } },
{ .data = { 0x00, 0x00 } },
{ .data = { 0x01, 0x7E } },
{ .data = { 0x03, 0x00 } },
{ .data = { 0x04, 0x65 } },
{ .data = { 0x0C, 0x74 } },
{ .data = { 0x17, 0x00 } },
{ .data = { 0x18, 0xB7 } },
{ .data = { 0x19, 0x00 } },
{ .data = { 0x1A, 0x00 } },
{ .data = { 0x1B, 0xB7 } },
{ .data = { 0x1C, 0x00 } },
{ .data = { 0x24, 0xFE } },
{ .data = { 0x37, 0x19 } },
{ .data = { 0x38, 0x05 } },
{ .data = { 0x39, 0x00 } },
{ .data = { 0x3A, 0x01 } },
{ .data = { 0x3B, 0x01 } },
{ .data = { 0x3C, 0x70 } },
{ .data = { 0x3D, 0xFF } },
{ .data = { 0x3E, 0xFF } },
{ .data = { 0x3F, 0xFF } },
{ .data = { 0x40, 0x06 } },
{ .data = { 0x41, 0xA0 } },
{ .data = { 0x43, 0x1E } },
{ .data = { 0x44, 0x0F } },
{ .data = { 0x45, 0x28 } },
{ .data = { 0x4B, 0x04 } },
{ .data = { 0x55, 0x02 } },
{ .data = { 0x56, 0x01 } },
{ .data = { 0x57, 0xA9 } },
{ .data = { 0x58, 0x0A } },
{ .data = { 0x59, 0x0A } },
{ .data = { 0x5A, 0x37 } },
{ .data = { 0x5B, 0x19 } },
{ .data = { 0x5D, 0x78 } },
{ .data = { 0x5E, 0x63 } },
{ .data = { 0x5F, 0x54 } },
{ .data = { 0x60, 0x49 } },
{ .data = { 0x61, 0x45 } },
{ .data = { 0x62, 0x38 } },
{ .data = { 0x63, 0x3D } },
{ .data = { 0x64, 0x28 } },
{ .data = { 0x65, 0x43 } },
{ .data = { 0x66, 0x41 } },
{ .data = { 0x67, 0x43 } },
{ .data = { 0x68, 0x62 } },
{ .data = { 0x69, 0x50 } },
{ .data = { 0x6A, 0x57 } },
{ .data = { 0x6B, 0x49 } },
{ .data = { 0x6C, 0x44 } },
{ .data = { 0x6D, 0x37 } },
{ .data = { 0x6E, 0x23 } },
{ .data = { 0x6F, 0x10 } },
{ .data = { 0x70, 0x78 } },
{ .data = { 0x71, 0x63 } },
{ .data = { 0x72, 0x54 } },
{ .data = { 0x73, 0x49 } },
{ .data = { 0x74, 0x45 } },
{ .data = { 0x75, 0x38 } },
{ .data = { 0x76, 0x3D } },
{ .data = { 0x77, 0x28 } },
{ .data = { 0x78, 0x43 } },
{ .data = { 0x79, 0x41 } },
{ .data = { 0x7A, 0x43 } },
{ .data = { 0x7B, 0x62 } },
{ .data = { 0x7C, 0x50 } },
{ .data = { 0x7D, 0x57 } },
{ .data = { 0x7E, 0x49 } },
{ .data = { 0x7F, 0x44 } },
{ .data = { 0x80, 0x37 } },
{ .data = { 0x81, 0x23 } },
{ .data = { 0x82, 0x10 } },
{ .data = { 0xE0, 0x02 } },
{ .data = { 0x00, 0x47 } },
{ .data = { 0x01, 0x47 } },
{ .data = { 0x02, 0x45 } },
{ .data = { 0x03, 0x45 } },
{ .data = { 0x04, 0x4B } },
{ .data = { 0x05, 0x4B } },
{ .data = { 0x06, 0x49 } },
{ .data = { 0x07, 0x49 } },
{ .data = { 0x08, 0x41 } },
{ .data = { 0x09, 0x1F } },
{ .data = { 0x0A, 0x1F } },
{ .data = { 0x0B, 0x1F } },
{ .data = { 0x0C, 0x1F } },
{ .data = { 0x0D, 0x1F } },
{ .data = { 0x0E, 0x1F } },
{ .data = { 0x0F, 0x5F } },
{ .data = { 0x10, 0x5F } },
{ .data = { 0x11, 0x57 } },
{ .data = { 0x12, 0x77 } },
{ .data = { 0x13, 0x35 } },
{ .data = { 0x14, 0x1F } },
{ .data = { 0x15, 0x1F } },
{ .data = { 0x16, 0x46 } },
{ .data = { 0x17, 0x46 } },
{ .data = { 0x18, 0x44 } },
{ .data = { 0x19, 0x44 } },
{ .data = { 0x1A, 0x4A } },
{ .data = { 0x1B, 0x4A } },
{ .data = { 0x1C, 0x48 } },
{ .data = { 0x1D, 0x48 } },
{ .data = { 0x1E, 0x40 } },
{ .data = { 0x1F, 0x1F } },
{ .data = { 0x20, 0x1F } },
{ .data = { 0x21, 0x1F } },
{ .data = { 0x22, 0x1F } },
{ .data = { 0x23, 0x1F } },
{ .data = { 0x24, 0x1F } },
{ .data = { 0x25, 0x5F } },
{ .data = { 0x26, 0x5F } },
{ .data = { 0x27, 0x57 } },
{ .data = { 0x28, 0x77 } },
{ .data = { 0x29, 0x35 } },
{ .data = { 0x2A, 0x1F } },
{ .data = { 0x2B, 0x1F } },
{ .data = { 0x58, 0x40 } },
{ .data = { 0x59, 0x00 } },
{ .data = { 0x5A, 0x00 } },
{ .data = { 0x5B, 0x10 } },
{ .data = { 0x5C, 0x06 } },
{ .data = { 0x5D, 0x40 } },
{ .data = { 0x5E, 0x01 } },
{ .data = { 0x5F, 0x02 } },
{ .data = { 0x60, 0x30 } },
{ .data = { 0x61, 0x01 } },
{ .data = { 0x62, 0x02 } },
{ .data = { 0x63, 0x03 } },
{ .data = { 0x64, 0x6B } },
{ .data = { 0x65, 0x05 } },
{ .data = { 0x66, 0x0C } },
{ .data = { 0x67, 0x73 } },
{ .data = { 0x68, 0x09 } },
{ .data = { 0x69, 0x03 } },
{ .data = { 0x6A, 0x56 } },
{ .data = { 0x6B, 0x08 } },
{ .data = { 0x6C, 0x00 } },
{ .data = { 0x6D, 0x04 } },
{ .data = { 0x6E, 0x04 } },
{ .data = { 0x6F, 0x88 } },
{ .data = { 0x70, 0x00 } },
{ .data = { 0x71, 0x00 } },
{ .data = { 0x72, 0x06 } },
{ .data = { 0x73, 0x7B } },
{ .data = { 0x74, 0x00 } },
{ .data = { 0x75, 0xF8 } },
{ .data = { 0x76, 0x00 } },
{ .data = { 0x77, 0xD5 } },
{ .data = { 0x78, 0x2E } },
{ .data = { 0x79, 0x12 } },
{ .data = { 0x7A, 0x03 } },
{ .data = { 0x7B, 0x00 } },
{ .data = { 0x7C, 0x00 } },
{ .data = { 0x7D, 0x03 } },
{ .data = { 0x7E, 0x7B } },
{ .data = { 0xE0, 0x04 } },
{ .data = { 0x00, 0x0E } },
{ .data = { 0x02, 0xB3 } },
{ .data = { 0x09, 0x60 } },
{ .data = { 0x0E, 0x2A } },
{ .data = { 0x36, 0x59 } },
{ .data = { 0xE0, 0x00 } },
static int radxa_display_8hd_ad002_init_cmds(struct jadard *jadard)
{
struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi };
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE1, 0x93);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE2, 0x65);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE3, 0xF8);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x03);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x7E);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x65);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0C, 0x74);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0xB7);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1A, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1B, 0xB7);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1C, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0xFE);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x19);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x05);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3A, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3B, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3C, 0x70);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3D, 0xFF);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3E, 0xFF);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3F, 0xFF);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x06);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0xA0);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1E);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x0F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x28);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4B, 0x04);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0xA9);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x0A);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x0A);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5A, 0x37);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5B, 0x19);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5D, 0x78);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5E, 0x63);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5F, 0x54);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x49);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x45);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x38);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x3D);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x28);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x43);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x41);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x43);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x62);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x50);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6A, 0x57);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6B, 0x49);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6C, 0x44);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6D, 0x37);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6E, 0x23);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6F, 0x10);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x78);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x63);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x54);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x49);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x45);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x38);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x3D);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x28);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x43);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x41);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7A, 0x43);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7B, 0x62);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7C, 0x50);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7D, 0x57);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7E, 0x49);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7F, 0x44);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x37);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x23);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x10);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x47);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x47);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x45);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x45);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x4B);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x4B);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x49);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x49);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x41);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0A, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0B, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0C, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0D, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0E, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0F, 0x5F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x5F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x57);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0x77);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x13, 0x35);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x46);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x46);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0x44);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x44);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1A, 0x4A);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1B, 0x4A);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1C, 0x48);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1D, 0x48);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1E, 0x40);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1F, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x5F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x5F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x57);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x77);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x35);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2A, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2B, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x40);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5A, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5B, 0x10);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5C, 0x06);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5D, 0x40);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5E, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5F, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x30);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x03);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x6B);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x05);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x0C);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x73);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x09);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x03);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6A, 0x56);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6B, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6C, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6D, 0x04);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6E, 0x04);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6F, 0x88);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x06);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x7B);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0xF8);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0xD5);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x2E);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x12);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7A, 0x03);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7B, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7C, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7D, 0x03);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7E, 0x7B);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x04);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x0E);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0xB3);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x60);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0E, 0x2A);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x59);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x00);
return dsi_ctx.accum_err;
};
static const struct jadard_panel_desc radxa_display_8hd_ad002_desc = {
......@@ -359,205 +391,209 @@ static const struct jadard_panel_desc radxa_display_8hd_ad002_desc = {
},
.lanes = 4,
.format = MIPI_DSI_FMT_RGB888,
.init_cmds = radxa_display_8hd_ad002_init_cmds,
.num_init_cmds = ARRAY_SIZE(radxa_display_8hd_ad002_init_cmds),
.init = radxa_display_8hd_ad002_init_cmds,
};
static const struct jadard_init_cmd cz101b4001_init_cmds[] = {
{ .data = { 0xE0, 0x00 } },
{ .data = { 0xE1, 0x93 } },
{ .data = { 0xE2, 0x65 } },
{ .data = { 0xE3, 0xF8 } },
{ .data = { 0x80, 0x03 } },
{ .data = { 0xE0, 0x01 } },
{ .data = { 0x00, 0x00 } },
{ .data = { 0x01, 0x3B } },
{ .data = { 0x0C, 0x74 } },
{ .data = { 0x17, 0x00 } },
{ .data = { 0x18, 0xAF } },
{ .data = { 0x19, 0x00 } },
{ .data = { 0x1A, 0x00 } },
{ .data = { 0x1B, 0xAF } },
{ .data = { 0x1C, 0x00 } },
{ .data = { 0x35, 0x26 } },
{ .data = { 0x37, 0x09 } },
{ .data = { 0x38, 0x04 } },
{ .data = { 0x39, 0x00 } },
{ .data = { 0x3A, 0x01 } },
{ .data = { 0x3C, 0x78 } },
{ .data = { 0x3D, 0xFF } },
{ .data = { 0x3E, 0xFF } },
{ .data = { 0x3F, 0x7F } },
{ .data = { 0x40, 0x06 } },
{ .data = { 0x41, 0xA0 } },
{ .data = { 0x42, 0x81 } },
{ .data = { 0x43, 0x14 } },
{ .data = { 0x44, 0x23 } },
{ .data = { 0x45, 0x28 } },
{ .data = { 0x55, 0x02 } },
{ .data = { 0x57, 0x69 } },
{ .data = { 0x59, 0x0A } },
{ .data = { 0x5A, 0x2A } },
{ .data = { 0x5B, 0x17 } },
{ .data = { 0x5D, 0x7F } },
{ .data = { 0x5E, 0x6B } },
{ .data = { 0x5F, 0x5C } },
{ .data = { 0x60, 0x4F } },
{ .data = { 0x61, 0x4D } },
{ .data = { 0x62, 0x3F } },
{ .data = { 0x63, 0x42 } },
{ .data = { 0x64, 0x2B } },
{ .data = { 0x65, 0x44 } },
{ .data = { 0x66, 0x43 } },
{ .data = { 0x67, 0x43 } },
{ .data = { 0x68, 0x63 } },
{ .data = { 0x69, 0x52 } },
{ .data = { 0x6A, 0x5A } },
{ .data = { 0x6B, 0x4F } },
{ .data = { 0x6C, 0x4E } },
{ .data = { 0x6D, 0x20 } },
{ .data = { 0x6E, 0x0F } },
{ .data = { 0x6F, 0x00 } },
{ .data = { 0x70, 0x7F } },
{ .data = { 0x71, 0x6B } },
{ .data = { 0x72, 0x5C } },
{ .data = { 0x73, 0x4F } },
{ .data = { 0x74, 0x4D } },
{ .data = { 0x75, 0x3F } },
{ .data = { 0x76, 0x42 } },
{ .data = { 0x77, 0x2B } },
{ .data = { 0x78, 0x44 } },
{ .data = { 0x79, 0x43 } },
{ .data = { 0x7A, 0x43 } },
{ .data = { 0x7B, 0x63 } },
{ .data = { 0x7C, 0x52 } },
{ .data = { 0x7D, 0x5A } },
{ .data = { 0x7E, 0x4F } },
{ .data = { 0x7F, 0x4E } },
{ .data = { 0x80, 0x20 } },
{ .data = { 0x81, 0x0F } },
{ .data = { 0x82, 0x00 } },
{ .data = { 0xE0, 0x02 } },
{ .data = { 0x00, 0x02 } },
{ .data = { 0x01, 0x02 } },
{ .data = { 0x02, 0x00 } },
{ .data = { 0x03, 0x00 } },
{ .data = { 0x04, 0x1E } },
{ .data = { 0x05, 0x1E } },
{ .data = { 0x06, 0x1F } },
{ .data = { 0x07, 0x1F } },
{ .data = { 0x08, 0x1F } },
{ .data = { 0x09, 0x17 } },
{ .data = { 0x0A, 0x17 } },
{ .data = { 0x0B, 0x37 } },
{ .data = { 0x0C, 0x37 } },
{ .data = { 0x0D, 0x47 } },
{ .data = { 0x0E, 0x47 } },
{ .data = { 0x0F, 0x45 } },
{ .data = { 0x10, 0x45 } },
{ .data = { 0x11, 0x4B } },
{ .data = { 0x12, 0x4B } },
{ .data = { 0x13, 0x49 } },
{ .data = { 0x14, 0x49 } },
{ .data = { 0x15, 0x1F } },
{ .data = { 0x16, 0x01 } },
{ .data = { 0x17, 0x01 } },
{ .data = { 0x18, 0x00 } },
{ .data = { 0x19, 0x00 } },
{ .data = { 0x1A, 0x1E } },
{ .data = { 0x1B, 0x1E } },
{ .data = { 0x1C, 0x1F } },
{ .data = { 0x1D, 0x1F } },
{ .data = { 0x1E, 0x1F } },
{ .data = { 0x1F, 0x17 } },
{ .data = { 0x20, 0x17 } },
{ .data = { 0x21, 0x37 } },
{ .data = { 0x22, 0x37 } },
{ .data = { 0x23, 0x46 } },
{ .data = { 0x24, 0x46 } },
{ .data = { 0x25, 0x44 } },
{ .data = { 0x26, 0x44 } },
{ .data = { 0x27, 0x4A } },
{ .data = { 0x28, 0x4A } },
{ .data = { 0x29, 0x48 } },
{ .data = { 0x2A, 0x48 } },
{ .data = { 0x2B, 0x1F } },
{ .data = { 0x2C, 0x01 } },
{ .data = { 0x2D, 0x01 } },
{ .data = { 0x2E, 0x00 } },
{ .data = { 0x2F, 0x00 } },
{ .data = { 0x30, 0x1F } },
{ .data = { 0x31, 0x1F } },
{ .data = { 0x32, 0x1E } },
{ .data = { 0x33, 0x1E } },
{ .data = { 0x34, 0x1F } },
{ .data = { 0x35, 0x17 } },
{ .data = { 0x36, 0x17 } },
{ .data = { 0x37, 0x37 } },
{ .data = { 0x38, 0x37 } },
{ .data = { 0x39, 0x08 } },
{ .data = { 0x3A, 0x08 } },
{ .data = { 0x3B, 0x0A } },
{ .data = { 0x3C, 0x0A } },
{ .data = { 0x3D, 0x04 } },
{ .data = { 0x3E, 0x04 } },
{ .data = { 0x3F, 0x06 } },
{ .data = { 0x40, 0x06 } },
{ .data = { 0x41, 0x1F } },
{ .data = { 0x42, 0x02 } },
{ .data = { 0x43, 0x02 } },
{ .data = { 0x44, 0x00 } },
{ .data = { 0x45, 0x00 } },
{ .data = { 0x46, 0x1F } },
{ .data = { 0x47, 0x1F } },
{ .data = { 0x48, 0x1E } },
{ .data = { 0x49, 0x1E } },
{ .data = { 0x4A, 0x1F } },
{ .data = { 0x4B, 0x17 } },
{ .data = { 0x4C, 0x17 } },
{ .data = { 0x4D, 0x37 } },
{ .data = { 0x4E, 0x37 } },
{ .data = { 0x4F, 0x09 } },
{ .data = { 0x50, 0x09 } },
{ .data = { 0x51, 0x0B } },
{ .data = { 0x52, 0x0B } },
{ .data = { 0x53, 0x05 } },
{ .data = { 0x54, 0x05 } },
{ .data = { 0x55, 0x07 } },
{ .data = { 0x56, 0x07 } },
{ .data = { 0x57, 0x1F } },
{ .data = { 0x58, 0x40 } },
{ .data = { 0x5B, 0x30 } },
{ .data = { 0x5C, 0x16 } },
{ .data = { 0x5D, 0x34 } },
{ .data = { 0x5E, 0x05 } },
{ .data = { 0x5F, 0x02 } },
{ .data = { 0x63, 0x00 } },
{ .data = { 0x64, 0x6A } },
{ .data = { 0x67, 0x73 } },
{ .data = { 0x68, 0x1D } },
{ .data = { 0x69, 0x08 } },
{ .data = { 0x6A, 0x6A } },
{ .data = { 0x6B, 0x08 } },
{ .data = { 0x6C, 0x00 } },
{ .data = { 0x6D, 0x00 } },
{ .data = { 0x6E, 0x00 } },
{ .data = { 0x6F, 0x88 } },
{ .data = { 0x75, 0xFF } },
{ .data = { 0x77, 0xDD } },
{ .data = { 0x78, 0x3F } },
{ .data = { 0x79, 0x15 } },
{ .data = { 0x7A, 0x17 } },
{ .data = { 0x7D, 0x14 } },
{ .data = { 0x7E, 0x82 } },
{ .data = { 0xE0, 0x04 } },
{ .data = { 0x00, 0x0E } },
{ .data = { 0x02, 0xB3 } },
{ .data = { 0x09, 0x61 } },
{ .data = { 0x0E, 0x48 } },
{ .data = { 0xE0, 0x00 } },
{ .data = { 0xE6, 0x02 } },
{ .data = { 0xE7, 0x0C } },
static int cz101b4001_init_cmds(struct jadard *jadard)
{
struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi };
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE1, 0x93);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE2, 0x65);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE3, 0xF8);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x03);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x3B);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0C, 0x74);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0xAF);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1A, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1B, 0xAF);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1C, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x26);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x09);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x04);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3A, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3C, 0x78);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3D, 0xFF);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3E, 0xFF);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3F, 0x7F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x06);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0xA0);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x81);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x14);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x23);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x28);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x69);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x0A);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5A, 0x2A);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5B, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5D, 0x7F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5E, 0x6B);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5F, 0x5C);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x4F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x4D);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x3F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x42);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x2B);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x44);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x43);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x43);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x63);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x52);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6A, 0x5A);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6B, 0x4F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6C, 0x4E);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6D, 0x20);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6E, 0x0F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6F, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x7F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x6B);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x5C);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x4F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x4D);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x3F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x42);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x2B);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x44);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x43);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7A, 0x43);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7B, 0x63);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7C, 0x52);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7D, 0x5A);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7E, 0x4F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7F, 0x4E);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x20);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x0F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x1E);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x1E);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0A, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0B, 0x37);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0C, 0x37);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0D, 0x47);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0E, 0x47);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0F, 0x45);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x45);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x4B);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0x4B);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x13, 0x49);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x49);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1A, 0x1E);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1B, 0x1E);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1C, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1D, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1E, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1F, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x37);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x37);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x46);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x46);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x44);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x44);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x4A);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x4A);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x48);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2A, 0x48);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2B, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2C, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2D, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2E, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2F, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0x1E);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0x1E);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x37);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x37);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3A, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3B, 0x0A);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3C, 0x0A);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3D, 0x04);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3E, 0x04);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3F, 0x06);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x06);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x1E);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0x1E);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4A, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4B, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4C, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4D, 0x37);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4E, 0x37);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4F, 0x09);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x09);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x0B);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x52, 0x0B);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x05);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0x05);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x07);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x07);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x1F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x40);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5B, 0x30);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5C, 0x16);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5D, 0x34);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5E, 0x05);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5F, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x6A);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x73);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x1D);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6A, 0x6A);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6B, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6C, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6D, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6E, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6F, 0x88);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0xFF);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0xDD);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x3F);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x15);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7A, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7D, 0x14);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7E, 0x82);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x04);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x0E);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0xB3);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x61);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0E, 0x48);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE0, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE6, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE7, 0x0C);
return dsi_ctx.accum_err;
};
static const struct jadard_panel_desc cz101b4001_desc = {
......@@ -580,8 +616,238 @@ static const struct jadard_panel_desc cz101b4001_desc = {
},
.lanes = 4,
.format = MIPI_DSI_FMT_RGB888,
.init_cmds = cz101b4001_init_cmds,
.num_init_cmds = ARRAY_SIZE(cz101b4001_init_cmds),
.init = cz101b4001_init_cmds,
};
static int kingdisplay_kd101ne3_init_cmds(struct jadard *jadard)
{
struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi };
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe0, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe1, 0x93);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe2, 0x65);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe3, 0xf8);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x03);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe0, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x74);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0xc7);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0xc7);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0xfe);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x19);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x28);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x05);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x12);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3c, 0x7e);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0xff);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3e, 0xff);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0x7f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x06);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0xa0);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1e);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x0b);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x6a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x0a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5a, 0x2e);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x1a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5c, 0x15);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x7f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x61);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x50);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x43);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x3f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x32);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x35);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x38);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x36);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x36);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x54);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x42);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x48);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x39);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x34);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x26);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x14);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x7f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x61);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x50);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x43);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x3f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x32);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x35);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x38);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x36);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7a, 0x36);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7b, 0x54);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7c, 0x42);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7d, 0x48);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7e, 0x39);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7f, 0x34);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x26);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x14);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe0, 0x02);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x52);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x50);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x77);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x57);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x4e);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x4c);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0a, 0x4a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0b, 0x48);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0d, 0x46);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x44);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0f, 0x40);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x13, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x53);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x51);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x77);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0x57);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1d, 0x4f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1e, 0x4d);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1f, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x4b);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x49);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x47);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x45);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x41);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2a, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x5f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2c, 0x13);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2d, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2e, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2f, 0x01);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0x0d);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0x0f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x05);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x07);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x09);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x0b);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3b, 0x11);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3c, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3e, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x12);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0x17);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0x0c);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4a, 0x0e);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4b, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4c, 0x04);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4d, 0x06);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4e, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4f, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x0a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x10);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x52, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x1f);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x40);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x10);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5c, 0x06);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x40);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x40);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x03);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x04);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x6c);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x6c);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x75);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0xb4);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x08);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x6c);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x6c);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x0c);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x88);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0xbb);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x00);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x05);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x2a);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe0, 0x04);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x0e);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0xb3);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x61);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x48);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe0, 0x00);
return dsi_ctx.accum_err;
};
static const struct jadard_panel_desc kingdisplay_kd101ne3_40ti_desc = {
.mode = {
.clock = (800 + 24 + 24 + 24) * (1280 + 30 + 4 + 8) * 60 / 1000,
.hdisplay = 800,
.hsync_start = 800 + 24,
.hsync_end = 800 + 24 + 24,
.htotal = 800 + 24 + 24 + 24,
.vdisplay = 1280,
.vsync_start = 1280 + 30,
.vsync_end = 1280 + 30 + 4,
.vtotal = 1280 + 30 + 4 + 8,
.width_mm = 135,
.height_mm = 216,
.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
},
.lanes = 4,
.format = MIPI_DSI_FMT_RGB888,
.init = kingdisplay_kd101ne3_init_cmds,
.lp11_before_reset = true,
.reset_before_power_off_vcioo = true,
.vcioo_to_lp11_delay_ms = 5,
.lp11_to_reset_delay_ms = 10,
.exit_sleep_to_display_on_delay_ms = 120,
.display_on_delay_ms = 20,
.backlight_off_to_display_off_delay_ms = 100,
.display_off_to_enter_sleep_delay_ms = 50,
.enter_sleep_to_reset_down_delay_ms = 100,
};
static int jadard_dsi_probe(struct mipi_dsi_device *dsi)
......@@ -622,6 +888,10 @@ static int jadard_dsi_probe(struct mipi_dsi_device *dsi)
drm_panel_init(&jadard->panel, dev, &jadard_funcs,
DRM_MODE_CONNECTOR_DSI);
ret = of_drm_get_panel_orientation(dev->of_node, &jadard->orientation);
if (ret < 0)
return dev_err_probe(dev, ret, "failed to get orientation\n");
ret = drm_panel_of_backlight(&jadard->panel);
if (ret)
return ret;
......@@ -652,6 +922,10 @@ static const struct of_device_id jadard_of_match[] = {
.compatible = "chongzhou,cz101b4001",
.data = &cz101b4001_desc
},
{
.compatible = "kingdisplay,kd101ne3-40ti",
.data = &kingdisplay_kd101ne3_40ti_desc
},
{
.compatible = "radxa,display-10hd-ad001",
.data = &cz101b4001_desc
......
......@@ -1067,6 +1067,30 @@ static const struct panel_desc auo_g104sn02 = {
.connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct drm_display_mode auo_g104stn01_mode = {
.clock = 40000,
.hdisplay = 800,
.hsync_start = 800 + 40,
.hsync_end = 800 + 40 + 88,
.htotal = 800 + 40 + 88 + 128,
.vdisplay = 600,
.vsync_start = 600 + 1,
.vsync_end = 600 + 1 + 23,
.vtotal = 600 + 1 + 23 + 4,
};
static const struct panel_desc auo_g104stn01 = {
.modes = &auo_g104stn01_mode,
.num_modes = 1,
.bpc = 8,
.size = {
.width = 211,
.height = 158,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
.connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct display_timing auo_g121ean01_timing = {
.pixelclock = { 60000000, 74400000, 90000000 },
.hactive = { 1280, 1280, 1280 },
......@@ -4534,6 +4558,9 @@ static const struct of_device_id platform_of_match[] = {
}, {
.compatible = "auo,g104sn02",
.data = &auo_g104sn02,
}, {
.compatible = "auo,g104stn01",
.data = &auo_g104stn01,
}, {
.compatible = "auo,g121ean01",
.data = &auo_g121ean01,
......
......@@ -838,3 +838,4 @@ module_platform_driver(panfrost_driver);
MODULE_AUTHOR("Panfrost Project Developers");
MODULE_DESCRIPTION("Panfrost DRM Driver");
MODULE_LICENSE("GPL v2");
MODULE_SOFTDEP("pre: governor_simpleondemand");
......@@ -2926,6 +2926,7 @@ queue_run_job(struct drm_sched_job *sched_job)
pm_runtime_get(ptdev->base.dev);
sched->pm.has_ref = true;
}
panthor_devfreq_record_busy(sched->ptdev);
}
done_fence = dma_fence_get(job->done_fence);
......
......@@ -20,3 +20,14 @@ config DRM_STM_DSI
select DRM_DW_MIPI_DSI
help
Choose this option for MIPI DSI support on STMicroelectronics SoC.
config DRM_STM_LVDS
tristate "STMicroelectronics LVDS Display Interface Transmitter DRM driver"
depends on DRM_STM
help
Enable support for LVDS encoders on STMicroelectronics SoC.
The STM LVDS is a bridge which serialize pixel stream onto
a LVDS protocol.
To compile this driver as a module, choose M here: the module will be
called lvds.
......@@ -5,4 +5,6 @@ stm-drm-y := \
obj-$(CONFIG_DRM_STM_DSI) += dw_mipi_dsi-stm.o
obj-$(CONFIG_DRM_STM_LVDS) += lvds.o
obj-$(CONFIG_DRM_STM) += stm-drm.o
......@@ -7,10 +7,13 @@
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <video/mipi_display.h>
......@@ -76,8 +79,12 @@ enum dsi_color {
struct dw_mipi_dsi_stm {
void __iomem *base;
struct device *dev;
struct clk *pllref_clk;
struct clk *pclk;
struct clk_hw txbyte_clk;
struct dw_mipi_dsi *dsi;
struct dw_mipi_dsi_plat_data pdata;
u32 hw_version;
int lane_min_kbps;
int lane_max_kbps;
......@@ -194,29 +201,198 @@ static int dsi_pll_get_params(struct dw_mipi_dsi_stm *dsi,
return 0;
}
static int dw_mipi_dsi_phy_init(void *priv_data)
#define clk_to_dw_mipi_dsi_stm(clk) \
container_of(clk, struct dw_mipi_dsi_stm, txbyte_clk)
static void dw_mipi_dsi_clk_disable(struct clk_hw *clk)
{
struct dw_mipi_dsi_stm *dsi = priv_data;
struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(clk);
DRM_DEBUG_DRIVER("\n");
/* Disable the DSI PLL */
dsi_clear(dsi, DSI_WRPCR, WRPCR_PLLEN);
/* Disable the regulator */
dsi_clear(dsi, DSI_WRPCR, WRPCR_REGEN | WRPCR_BGREN);
}
static int dw_mipi_dsi_clk_enable(struct clk_hw *clk)
{
struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(clk);
u32 val;
int ret;
DRM_DEBUG_DRIVER("\n");
/* Enable the regulator */
dsi_set(dsi, DSI_WRPCR, WRPCR_REGEN | WRPCR_BGREN);
ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_RRS,
SLEEP_US, TIMEOUT_US);
ret = readl_poll_timeout_atomic(dsi->base + DSI_WISR, val, val & WISR_RRS,
SLEEP_US, TIMEOUT_US);
if (ret)
DRM_DEBUG_DRIVER("!TIMEOUT! waiting REGU, let's continue\n");
/* Enable the DSI PLL & wait for its lock */
dsi_set(dsi, DSI_WRPCR, WRPCR_PLLEN);
ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_PLLLS,
SLEEP_US, TIMEOUT_US);
ret = readl_poll_timeout_atomic(dsi->base + DSI_WISR, val, val & WISR_PLLLS,
SLEEP_US, TIMEOUT_US);
if (ret)
DRM_DEBUG_DRIVER("!TIMEOUT! waiting PLL, let's continue\n");
return 0;
}
static int dw_mipi_dsi_clk_is_enabled(struct clk_hw *hw)
{
struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(hw);
return dsi_read(dsi, DSI_WRPCR) & WRPCR_PLLEN;
}
static unsigned long dw_mipi_dsi_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(hw);
unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz;
u32 val;
DRM_DEBUG_DRIVER("\n");
pll_in_khz = (unsigned int)(parent_rate / 1000);
val = dsi_read(dsi, DSI_WRPCR);
idf = (val & WRPCR_IDF) >> 11;
if (!idf)
idf = 1;
ndiv = (val & WRPCR_NDIV) >> 2;
odf = int_pow(2, (val & WRPCR_ODF) >> 16);
/* Get the adjusted pll out value */
pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf);
return (unsigned long)pll_out_khz * 1000;
}
static long dw_mipi_dsi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(hw);
unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz;
int ret;
DRM_DEBUG_DRIVER("\n");
pll_in_khz = (unsigned int)(*parent_rate / 1000);
/* Compute best pll parameters */
idf = 0;
ndiv = 0;
odf = 0;
ret = dsi_pll_get_params(dsi, pll_in_khz, rate / 1000,
&idf, &ndiv, &odf);
if (ret)
DRM_WARN("Warning dsi_pll_get_params(): bad params\n");
/* Get the adjusted pll out value */
pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf);
return pll_out_khz * 1000;
}
static int dw_mipi_dsi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(hw);
unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz;
int ret;
u32 val;
DRM_DEBUG_DRIVER("\n");
pll_in_khz = (unsigned int)(parent_rate / 1000);
/* Compute best pll parameters */
idf = 0;
ndiv = 0;
odf = 0;
ret = dsi_pll_get_params(dsi, pll_in_khz, rate / 1000, &idf, &ndiv, &odf);
if (ret)
DRM_WARN("Warning dsi_pll_get_params(): bad params\n");
/* Get the adjusted pll out value */
pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf);
/* Set the PLL division factors */
dsi_update_bits(dsi, DSI_WRPCR, WRPCR_NDIV | WRPCR_IDF | WRPCR_ODF,
(ndiv << 2) | (idf << 11) | ((ffs(odf) - 1) << 16));
/* Compute uix4 & set the bit period in high-speed mode */
val = 4000000 / pll_out_khz;
dsi_update_bits(dsi, DSI_WPCR0, WPCR0_UIX4, val);
return 0;
}
static void dw_mipi_dsi_clk_unregister(void *data)
{
struct dw_mipi_dsi_stm *dsi = data;
DRM_DEBUG_DRIVER("\n");
of_clk_del_provider(dsi->dev->of_node);
clk_hw_unregister(&dsi->txbyte_clk);
}
static const struct clk_ops dw_mipi_dsi_stm_clk_ops = {
.enable = dw_mipi_dsi_clk_enable,
.disable = dw_mipi_dsi_clk_disable,
.is_enabled = dw_mipi_dsi_clk_is_enabled,
.recalc_rate = dw_mipi_dsi_clk_recalc_rate,
.round_rate = dw_mipi_dsi_clk_round_rate,
.set_rate = dw_mipi_dsi_clk_set_rate,
};
static struct clk_init_data cdata_init = {
.name = "ck_dsi_phy",
.ops = &dw_mipi_dsi_stm_clk_ops,
.parent_names = (const char * []) {"ck_hse"},
.num_parents = 1,
};
static int dw_mipi_dsi_clk_register(struct dw_mipi_dsi_stm *dsi,
struct device *dev)
{
struct device_node *node = dev->of_node;
int ret;
DRM_DEBUG_DRIVER("Registering clk\n");
dsi->txbyte_clk.init = &cdata_init;
ret = clk_hw_register(dev, &dsi->txbyte_clk);
if (ret)
return ret;
ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get,
&dsi->txbyte_clk);
if (ret)
clk_hw_unregister(&dsi->txbyte_clk);
return ret;
}
static int dw_mipi_dsi_phy_init(void *priv_data)
{
struct dw_mipi_dsi_stm *dsi = priv_data;
int ret;
ret = clk_prepare_enable(dsi->txbyte_clk.clk);
return ret;
}
static void dw_mipi_dsi_phy_power_on(void *priv_data)
{
struct dw_mipi_dsi_stm *dsi = priv_data;
......@@ -233,6 +409,8 @@ static void dw_mipi_dsi_phy_power_off(void *priv_data)
DRM_DEBUG_DRIVER("\n");
clk_disable_unprepare(dsi->txbyte_clk.clk);
/* Disable the DSI wrapper */
dsi_clear(dsi, DSI_WCR, WCR_DSIEN);
}
......@@ -243,9 +421,8 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode,
unsigned int *lane_mbps)
{
struct dw_mipi_dsi_stm *dsi = priv_data;
unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz;
unsigned int pll_in_khz, pll_out_khz;
int ret, bpp;
u32 val;
pll_in_khz = (unsigned int)(clk_get_rate(dsi->pllref_clk) / 1000);
......@@ -266,25 +443,10 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode,
DRM_WARN("Warning min phy mbps is used\n");
}
/* Compute best pll parameters */
idf = 0;
ndiv = 0;
odf = 0;
ret = dsi_pll_get_params(dsi, pll_in_khz, pll_out_khz,
&idf, &ndiv, &odf);
ret = clk_set_rate((dsi->txbyte_clk.clk), pll_out_khz * 1000);
if (ret)
DRM_WARN("Warning dsi_pll_get_params(): bad params\n");
/* Get the adjusted pll out value */
pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf);
/* Set the PLL division factors */
dsi_update_bits(dsi, DSI_WRPCR, WRPCR_NDIV | WRPCR_IDF | WRPCR_ODF,
(ndiv << 2) | (idf << 11) | ((ffs(odf) - 1) << 16));
/* Compute uix4 & set the bit period in high-speed mode */
val = 4000000 / pll_out_khz;
dsi_update_bits(dsi, DSI_WPCR0, WPCR0_UIX4, val);
DRM_DEBUG_DRIVER("ERROR Could not set rate of %d to %s clk->name",
pll_out_khz, clk_hw_get_name(&dsi->txbyte_clk));
/* Select video mode by resetting DSIM bit */
dsi_clear(dsi, DSI_WCFGR, WCFGR_DSIM);
......@@ -443,7 +605,7 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dw_mipi_dsi_stm *dsi;
struct clk *pclk;
const struct dw_mipi_dsi_plat_data *pdata = of_device_get_match_data(dev);
int ret;
dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
......@@ -483,21 +645,21 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev)
goto err_clk_get;
}
pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(pclk)) {
ret = PTR_ERR(pclk);
dsi->pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(dsi->pclk)) {
ret = PTR_ERR(dsi->pclk);
DRM_ERROR("Unable to get peripheral clock: %d\n", ret);
goto err_dsi_probe;
}
ret = clk_prepare_enable(pclk);
ret = clk_prepare_enable(dsi->pclk);
if (ret) {
DRM_ERROR("%s: Failed to enable peripheral clk\n", __func__);
goto err_dsi_probe;
}
dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION;
clk_disable_unprepare(pclk);
clk_disable_unprepare(dsi->pclk);
if (dsi->hw_version != HWVER_130 && dsi->hw_version != HWVER_131) {
ret = -ENODEV;
......@@ -513,18 +675,41 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev)
dsi->lane_max_kbps *= 2;
}
dw_mipi_dsi_stm_plat_data.base = dsi->base;
dw_mipi_dsi_stm_plat_data.priv_data = dsi;
dsi->pdata = *pdata;
dsi->pdata.base = dsi->base;
dsi->pdata.priv_data = dsi;
dsi->pdata.max_data_lanes = 2;
dsi->pdata.phy_ops = &dw_mipi_dsi_stm_phy_ops;
platform_set_drvdata(pdev, dsi);
dsi->dsi = dw_mipi_dsi_probe(pdev, &dw_mipi_dsi_stm_plat_data);
dsi->dsi = dw_mipi_dsi_probe(pdev, &dsi->pdata);
if (IS_ERR(dsi->dsi)) {
ret = PTR_ERR(dsi->dsi);
dev_err_probe(dev, ret, "Failed to initialize mipi dsi host\n");
goto err_dsi_probe;
}
/*
* We need to wait for the generic bridge to probe before enabling and
* register the internal pixel clock.
*/
ret = clk_prepare_enable(dsi->pclk);
if (ret) {
DRM_ERROR("%s: Failed to enable peripheral clk\n", __func__);
goto err_dsi_probe;
}
ret = dw_mipi_dsi_clk_register(dsi, dev);
if (ret) {
DRM_ERROR("Failed to register DSI pixel clock: %d\n", ret);
clk_disable_unprepare(dsi->pclk);
goto err_dsi_probe;
}
clk_disable_unprepare(dsi->pclk);
return 0;
err_dsi_probe:
......@@ -541,24 +726,26 @@ static void dw_mipi_dsi_stm_remove(struct platform_device *pdev)
dw_mipi_dsi_remove(dsi->dsi);
clk_disable_unprepare(dsi->pllref_clk);
dw_mipi_dsi_clk_unregister(dsi);
regulator_disable(dsi->vdd_supply);
}
static int __maybe_unused dw_mipi_dsi_stm_suspend(struct device *dev)
static int dw_mipi_dsi_stm_suspend(struct device *dev)
{
struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data;
struct dw_mipi_dsi_stm *dsi = dev_get_drvdata(dev);
DRM_DEBUG_DRIVER("\n");
clk_disable_unprepare(dsi->pllref_clk);
clk_disable_unprepare(dsi->pclk);
regulator_disable(dsi->vdd_supply);
return 0;
}
static int __maybe_unused dw_mipi_dsi_stm_resume(struct device *dev)
static int dw_mipi_dsi_stm_resume(struct device *dev)
{
struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data;
struct dw_mipi_dsi_stm *dsi = dev_get_drvdata(dev);
int ret;
DRM_DEBUG_DRIVER("\n");
......@@ -569,8 +756,16 @@ static int __maybe_unused dw_mipi_dsi_stm_resume(struct device *dev)
return ret;
}
ret = clk_prepare_enable(dsi->pclk);
if (ret) {
regulator_disable(dsi->vdd_supply);
DRM_ERROR("Failed to enable pclk: %d\n", ret);
return ret;
}
ret = clk_prepare_enable(dsi->pllref_clk);
if (ret) {
clk_disable_unprepare(dsi->pclk);
regulator_disable(dsi->vdd_supply);
DRM_ERROR("Failed to enable pllref_clk: %d\n", ret);
return ret;
......@@ -580,8 +775,10 @@ static int __maybe_unused dw_mipi_dsi_stm_resume(struct device *dev)
}
static const struct dev_pm_ops dw_mipi_dsi_stm_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(dw_mipi_dsi_stm_suspend,
dw_mipi_dsi_stm_resume)
SYSTEM_SLEEP_PM_OPS(dw_mipi_dsi_stm_suspend,
dw_mipi_dsi_stm_resume)
RUNTIME_PM_OPS(dw_mipi_dsi_stm_suspend,
dw_mipi_dsi_stm_resume, NULL)
};
static struct platform_driver dw_mipi_dsi_stm_driver = {
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2023, STMicroelectronics - All Rights Reserved
* Author(s): Raphaël GALLAIS-POU <raphael.gallais-pou@foss.st.com> for STMicroelectronics.
*/
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_device.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/media-bus-format.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
/* LVDS Host registers */
#define LVDS_CR 0x0000 /* configuration register */
#define LVDS_DMLCR0 0x0004 /* data mapping lsb configuration register 0 */
#define LVDS_DMMCR0 0x0008 /* data mapping msb configuration register 0 */
#define LVDS_DMLCR1 0x000C /* data mapping lsb configuration register 1 */
#define LVDS_DMMCR1 0x0010 /* data mapping msb configuration register 1 */
#define LVDS_DMLCR2 0x0014 /* data mapping lsb configuration register 2 */
#define LVDS_DMMCR2 0x0018 /* data mapping msb configuration register 2 */
#define LVDS_DMLCR3 0x001C /* data mapping lsb configuration register 3 */
#define LVDS_DMMCR3 0x0020 /* data mapping msb configuration register 3 */
#define LVDS_DMLCR4 0x0024 /* data mapping lsb configuration register 4 */
#define LVDS_DMMCR4 0x0028 /* data mapping msb configuration register 4 */
#define LVDS_CDL1CR 0x002C /* channel distrib link 1 configuration register */
#define LVDS_CDL2CR 0x0030 /* channel distrib link 2 configuration register */
#define CDL1CR_DEFAULT 0x04321 /* Default value for CDL1CR */
#define CDL2CR_DEFAULT 0x59876 /* Default value for CDL2CR */
#define LVDS_DMLCR(bit) (LVDS_DMLCR0 + 0x8 * (bit))
#define LVDS_DMMCR(bit) (LVDS_DMMCR0 + 0x8 * (bit))
/* LVDS Wrapper registers */
#define LVDS_WCLKCR 0x11B0 /* Wrapper clock control register */
#define LVDS_HWCFGR 0x1FF0 /* HW configuration register */
#define LVDS_VERR 0x1FF4 /* Version register */
#define LVDS_IPIDR 0x1FF8 /* Identification register */
#define LVDS_SIDR 0x1FFC /* Size Identification register */
/* Bitfield description */
#define CR_LVDSEN BIT(0) /* LVDS PHY Enable */
#define CR_HSPOL BIT(1) /* Horizontal Synchronization Polarity */
#define CR_VSPOL BIT(2) /* Vertical Synchronization Polarity */
#define CR_DEPOL BIT(3) /* Data Enable Polarity */
#define CR_CI BIT(4) /* Control Internal (software controlled bit) */
#define CR_LKMOD BIT(5) /* Link Mode, for both Links */
#define CR_LKPHA BIT(6) /* Link Phase, for both Links */
#define CR_LK1POL GENMASK(20, 16) /* Link-1 output Polarity */
#define CR_LK2POL GENMASK(25, 21) /* Link-2 output Polarity */
#define DMMCR_MAP0 GENMASK(4, 0) /* Mapping for bit 0 of datalane x */
#define DMMCR_MAP1 GENMASK(9, 5) /* Mapping for bit 1 of datalane x */
#define DMMCR_MAP2 GENMASK(14, 10) /* Mapping for bit 2 of datalane x */
#define DMMCR_MAP3 GENMASK(19, 15) /* Mapping for bit 3 of datalane x */
#define DMLCR_MAP4 GENMASK(4, 0) /* Mapping for bit 4 of datalane x */
#define DMLCR_MAP5 GENMASK(9, 5) /* Mapping for bit 5 of datalane x */
#define DMLCR_MAP6 GENMASK(14, 10) /* Mapping for bit 6 of datalane x */
#define CDLCR_DISTR0 GENMASK(3, 0) /* Channel distribution for lane 0 */
#define CDLCR_DISTR1 GENMASK(7, 4) /* Channel distribution for lane 1 */
#define CDLCR_DISTR2 GENMASK(11, 8) /* Channel distribution for lane 2 */
#define CDLCR_DISTR3 GENMASK(15, 12) /* Channel distribution for lane 3 */
#define CDLCR_DISTR4 GENMASK(19, 16) /* Channel distribution for lane 4 */
#define PHY_GCR_BIT_CLK_OUT BIT(0) /* BIT clock enable */
#define PHY_GCR_LS_CLK_OUT BIT(4) /* LS clock enable */
#define PHY_GCR_DP_CLK_OUT BIT(8) /* DP clock enable */
#define PHY_GCR_RSTZ BIT(24) /* LVDS PHY digital reset */
#define PHY_GCR_DIV_RSTN BIT(25) /* Output divider reset */
#define PHY_SCR_TX_EN BIT(16) /* Transmission mode enable */
/* Current mode driver enable */
#define PHY_CMCR_CM_EN_DL (BIT(28) | BIT(20) | BIT(12) | BIT(4))
#define PHY_CMCR_CM_EN_DL4 BIT(4)
/* Bias enable */
#define PHY_BCR1_EN_BIAS_DL (BIT(16) | BIT(12) | BIT(8) | BIT(4) | BIT(0))
#define PHY_BCR2_BIAS_EN BIT(28)
/* Voltage mode driver enable */
#define PHY_BCR3_VM_EN_DL (BIT(16) | BIT(12) | BIT(8) | BIT(4) | BIT(0))
#define PHY_DCR_POWER_OK BIT(12)
#define PHY_CFGCR_EN_DIG_DL GENMASK(4, 0) /* LVDS PHY digital lane enable */
#define PHY_PLLCR1_PLL_EN BIT(0) /* LVDS PHY PLL enable */
#define PHY_PLLCR1_EN_SD BIT(1) /* LVDS PHY PLL sigma-delta signal enable */
#define PHY_PLLCR1_EN_TWG BIT(2) /* LVDS PHY PLL triangular wave generator enable */
#define PHY_PLLCR1_DIV_EN BIT(8) /* LVDS PHY PLL dividers enable */
#define PHY_PLLCR2_NDIV GENMASK(25, 16) /* NDIV mask value */
#define PHY_PLLCR2_BDIV GENMASK(9, 0) /* BDIV mask value */
#define PHY_PLLSR_PLL_LOCK BIT(0) /* LVDS PHY PLL lock status */
#define PHY_PLLSDCR1_MDIV GENMASK(9, 0) /* MDIV mask value */
#define PHY_PLLTESTCR_TDIV GENMASK(25, 16) /* TDIV mask value */
#define PHY_PLLTESTCR_CLK_EN BIT(0) /* Test clock enable */
#define PHY_PLLTESTCR_EN BIT(8) /* Test divider output enable */
#define WCLKCR_SECND_CLKPIX_SEL BIT(0) /* Pixel clock selection */
#define WCLKCR_SRCSEL BIT(8) /* Source selection for the pixel clock */
/* Sleep & timeout for pll lock/unlock */
#define SLEEP_US 1000
#define TIMEOUT_US 200000
/*
* The link phase defines whether an ODD pixel is carried over together with
* the next EVEN pixel or together with the previous EVEN pixel.
*
* LVDS_DUAL_LINK_EVEN_ODD_PIXELS (LKPHA = 0)
*
* ,--------. ,--------. ,--------. ,--------. ,---------.
* | ODD LK \/ PIXEL 3 \/ PIXEL 1 \/ PIXEL' 1 \/ PIXEL' 3 |
* | EVEN LK /\ PIXEL 2 /\ PIXEL' 0 /\ PIXEL' 2 /\ PIXEL' 4 |
* `--------' `--------' `--------' `--------' `---------'
*
* LVDS_DUAL_LINK_ODD_EVEN_PIXELS (LKPHA = 1)
*
* ,--------. ,--------. ,--------. ,--------. ,---------.
* | ODD LK \/ PIXEL 3 \/ PIXEL 1 \/ PIXEL' 1 \/ PIXEL' 3 |
* | EVEN LK /\ PIXEL 4 /\ PIXEL 2 /\ PIXEL' 0 /\ PIXEL' 2 |
* `--------' `--------' `--------' `--------' `---------'
*
*/
enum lvds_link_type {
LVDS_SINGLE_LINK_PRIMARY = 0,
LVDS_SINGLE_LINK_SECONDARY,
LVDS_DUAL_LINK_EVEN_ODD_PIXELS,
LVDS_DUAL_LINK_ODD_EVEN_PIXELS,
};
enum lvds_pixel {
PIX_R_0 = 0,
PIX_R_1,
PIX_R_2,
PIX_R_3,
PIX_R_4,
PIX_R_5,
PIX_R_6,
PIX_R_7,
PIX_G_0,
PIX_G_1,
PIX_G_2,
PIX_G_3,
PIX_G_4,
PIX_G_5,
PIX_G_6,
PIX_G_7,
PIX_B_0,
PIX_B_1,
PIX_B_2,
PIX_B_3,
PIX_B_4,
PIX_B_5,
PIX_B_6,
PIX_B_7,
PIX_H_S,
PIX_V_S,
PIX_D_E,
PIX_C_E,
PIX_C_I,
PIX_TOG,
PIX_ONE,
PIX_ZER,
};
struct phy_reg_offsets {
u32 GCR; /* Global Control Register */
u32 CMCR1; /* Current Mode Control Register 1 */
u32 CMCR2; /* Current Mode Control Register 2 */
u32 SCR; /* Serial Control Register */
u32 BCR1; /* Bias Control Register 1 */
u32 BCR2; /* Bias Control Register 2 */
u32 BCR3; /* Bias Control Register 3 */
u32 MPLCR; /* Monitor PLL Lock Control Register */
u32 DCR; /* Debug Control Register */
u32 SSR1; /* Spare Status Register 1 */
u32 CFGCR; /* Configuration Control Register */
u32 PLLCR1; /* PLL_MODE 1 Control Register */
u32 PLLCR2; /* PLL_MODE 2 Control Register */
u32 PLLSR; /* PLL Status Register */
u32 PLLSDCR1; /* PLL_SD_1 Control Register */
u32 PLLSDCR2; /* PLL_SD_2 Control Register */
u32 PLLTWGCR1;/* PLL_TWG_1 Control Register */
u32 PLLTWGCR2;/* PLL_TWG_2 Control Register */
u32 PLLCPCR; /* PLL_CP Control Register */
u32 PLLTESTCR;/* PLL_TEST Control Register */
};
struct lvds_phy_info {
u32 base;
struct phy_reg_offsets ofs;
};
static struct lvds_phy_info lvds_phy_16ff_primary = {
.base = 0x1000,
.ofs = {
.GCR = 0x0,
.CMCR1 = 0xC,
.CMCR2 = 0x10,
.SCR = 0x20,
.BCR1 = 0x2C,
.BCR2 = 0x30,
.BCR3 = 0x34,
.MPLCR = 0x64,
.DCR = 0x84,
.SSR1 = 0x88,
.CFGCR = 0xA0,
.PLLCR1 = 0xC0,
.PLLCR2 = 0xC4,
.PLLSR = 0xC8,
.PLLSDCR1 = 0xCC,
.PLLSDCR2 = 0xD0,
.PLLTWGCR1 = 0xD4,
.PLLTWGCR2 = 0xD8,
.PLLCPCR = 0xE0,
.PLLTESTCR = 0xE8,
}
};
static struct lvds_phy_info lvds_phy_16ff_secondary = {
.base = 0x1100,
.ofs = {
.GCR = 0x0,
.CMCR1 = 0xC,
.CMCR2 = 0x10,
.SCR = 0x20,
.BCR1 = 0x2C,
.BCR2 = 0x30,
.BCR3 = 0x34,
.MPLCR = 0x64,
.DCR = 0x84,
.SSR1 = 0x88,
.CFGCR = 0xA0,
.PLLCR1 = 0xC0,
.PLLCR2 = 0xC4,
.PLLSR = 0xC8,
.PLLSDCR1 = 0xCC,
.PLLSDCR2 = 0xD0,
.PLLTWGCR1 = 0xD4,
.PLLTWGCR2 = 0xD8,
.PLLCPCR = 0xE0,
.PLLTESTCR = 0xE8,
}
};
struct stm_lvds {
void __iomem *base;
struct device *dev;
struct clk *pclk; /* APB peripheral clock */
struct clk *pllref_clk; /* Reference clock for the internal PLL */
struct clk_hw lvds_ck_px; /* Pixel clock */
u32 pixel_clock_rate; /* Pixel clock rate */
struct lvds_phy_info *primary;
struct lvds_phy_info *secondary;
struct drm_bridge lvds_bridge;
struct drm_bridge *next_bridge;
struct drm_connector connector;
struct drm_encoder *encoder;
struct drm_panel *panel;
u32 hw_version;
u32 link_type;
};
#define bridge_to_stm_lvds(b) \
container_of(b, struct stm_lvds, lvds_bridge)
#define connector_to_stm_lvds(c) \
container_of(c, struct stm_lvds, connector)
#define lvds_is_dual_link(lvds) \
({ \
typeof(lvds) __lvds = (lvds); \
__lvds == LVDS_DUAL_LINK_EVEN_ODD_PIXELS || \
__lvds == LVDS_DUAL_LINK_ODD_EVEN_PIXELS; \
})
static inline void lvds_write(struct stm_lvds *lvds, u32 reg, u32 val)
{
writel(val, lvds->base + reg);
}
static inline u32 lvds_read(struct stm_lvds *lvds, u32 reg)
{
return readl(lvds->base + reg);
}
static inline void lvds_set(struct stm_lvds *lvds, u32 reg, u32 mask)
{
lvds_write(lvds, reg, lvds_read(lvds, reg) | mask);
}
static inline void lvds_clear(struct stm_lvds *lvds, u32 reg, u32 mask)
{
lvds_write(lvds, reg, lvds_read(lvds, reg) & ~mask);
}
/*
* Expected JEIDA-RGB888 data to be sent in LSB format
* bit6 ............................bit0
* CHAN0 {ONE, ONE, ZERO, ZERO, ZERO, ONE, ONE}
* CHAN1 {G2, R7, R6, R5, R4, R3, R2}
* CHAN2 {B3, B2, G7, G6, G5, G4, G3}
* CHAN3 {DE, VS, HS, B7, B6, B5, B4}
* CHAN4 {CE, B1, B0, G1, G0, R1, R0}
*/
static enum lvds_pixel lvds_bitmap_jeida_rgb888[5][7] = {
{ PIX_ONE, PIX_ONE, PIX_ZER, PIX_ZER, PIX_ZER, PIX_ONE, PIX_ONE },
{ PIX_G_2, PIX_R_7, PIX_R_6, PIX_R_5, PIX_R_4, PIX_R_3, PIX_R_2 },
{ PIX_B_3, PIX_B_2, PIX_G_7, PIX_G_6, PIX_G_5, PIX_G_4, PIX_G_3 },
{ PIX_D_E, PIX_V_S, PIX_H_S, PIX_B_7, PIX_B_6, PIX_B_5, PIX_B_4 },
{ PIX_C_E, PIX_B_1, PIX_B_0, PIX_G_1, PIX_G_0, PIX_R_1, PIX_R_0 }
};
/*
* Expected VESA-RGB888 data to be sent in LSB format
* bit6 ............................bit0
* CHAN0 {ONE, ONE, ZERO, ZERO, ZERO, ONE, ONE}
* CHAN1 {G0, R5, R4, R3, R2, R1, R0}
* CHAN2 {B1, B0, G5, G4, G3, G2, G1}
* CHAN3 {DE, VS, HS, B5, B4, B3, B2}
* CHAN4 {CE, B7, B6, G7, G6, R7, R6}
*/
static enum lvds_pixel lvds_bitmap_vesa_rgb888[5][7] = {
{ PIX_ONE, PIX_ONE, PIX_ZER, PIX_ZER, PIX_ZER, PIX_ONE, PIX_ONE },
{ PIX_G_0, PIX_R_5, PIX_R_4, PIX_R_3, PIX_R_2, PIX_R_1, PIX_R_0 },
{ PIX_B_1, PIX_B_0, PIX_G_5, PIX_G_4, PIX_G_3, PIX_G_2, PIX_G_1 },
{ PIX_D_E, PIX_V_S, PIX_H_S, PIX_B_5, PIX_B_4, PIX_B_3, PIX_B_2 },
{ PIX_C_E, PIX_B_7, PIX_B_6, PIX_G_7, PIX_G_6, PIX_R_7, PIX_R_6 }
};
/*
* Clocks and PHY related functions
*/
static int lvds_pll_enable(struct stm_lvds *lvds, struct lvds_phy_info *phy)
{
struct drm_device *drm = lvds->lvds_bridge.dev;
u32 lvds_gcr;
int val, ret;
/*
* PLL lock timing control for the monitor unmask after startup (pll_en)
* Adjusted value so that the masking window is opened at start-up
*/
lvds_write(lvds, phy->base + phy->ofs.MPLCR, (0x200 - 0x160) << 16);
/* Enable bias */
lvds_write(lvds, phy->base + phy->ofs.BCR2, PHY_BCR2_BIAS_EN);
/* Enable DP, LS, BIT clock output */
lvds_gcr = PHY_GCR_DP_CLK_OUT | PHY_GCR_LS_CLK_OUT | PHY_GCR_BIT_CLK_OUT;
lvds_set(lvds, phy->base + phy->ofs.GCR, lvds_gcr);
/* Power up all output dividers */
lvds_set(lvds, phy->base + phy->ofs.PLLTESTCR, PHY_PLLTESTCR_EN);
lvds_set(lvds, phy->base + phy->ofs.PLLCR1, PHY_PLLCR1_DIV_EN);
/* Set PHY in serial transmission mode */
lvds_set(lvds, phy->base + phy->ofs.SCR, PHY_SCR_TX_EN);
/* Enable the LVDS PLL & wait for its lock */
lvds_set(lvds, phy->base + phy->ofs.PLLCR1, PHY_PLLCR1_PLL_EN);
ret = readl_poll_timeout_atomic(lvds->base + phy->base + phy->ofs.PLLSR,
val, val & PHY_PLLSR_PLL_LOCK,
SLEEP_US, TIMEOUT_US);
if (ret)
drm_err(drm, "!TIMEOUT! waiting PLL, let's continue\n");
/* WCLKCR_SECND_CLKPIX_SEL is for dual link */
lvds_write(lvds, LVDS_WCLKCR, WCLKCR_SECND_CLKPIX_SEL);
lvds_set(lvds, phy->ofs.PLLTESTCR, PHY_PLLTESTCR_CLK_EN);
return ret;
}
static int pll_get_clkout_khz(int clkin_khz, int bdiv, int mdiv, int ndiv)
{
int divisor = ndiv * bdiv;
/* Prevents from division by 0 */
if (!divisor)
return 0;
return clkin_khz * mdiv / divisor;
}
#define TDIV 70
#define NDIV_MIN 2
#define NDIV_MAX 6
#define BDIV_MIN 2
#define BDIV_MAX 6
#define MDIV_MIN 1
#define MDIV_MAX 1023
static int lvds_pll_get_params(struct stm_lvds *lvds,
unsigned int clkin_khz, unsigned int clkout_khz,
unsigned int *bdiv, unsigned int *mdiv, unsigned int *ndiv)
{
int delta, best_delta; /* all in khz */
int i, o, n;
/* Early checks preventing division by 0 & odd results */
if (clkin_khz <= 0 || clkout_khz <= 0)
return -EINVAL;
best_delta = 1000000; /* big started value (1000000khz) */
for (i = NDIV_MIN; i <= NDIV_MAX; i++) {
for (o = BDIV_MIN; o <= BDIV_MAX; o++) {
n = DIV_ROUND_CLOSEST(i * o * clkout_khz, clkin_khz);
/* Check ndiv according to vco range */
if (n < MDIV_MIN || n > MDIV_MAX)
continue;
/* Check if new delta is better & saves parameters */
delta = pll_get_clkout_khz(clkin_khz, i, n, o) - clkout_khz;
if (delta < 0)
delta = -delta;
if (delta < best_delta) {
*ndiv = i;
*mdiv = n;
*bdiv = o;
best_delta = delta;
}
/* fast return in case of "perfect result" */
if (!delta)
return 0;
}
}
return 0;
}
static void lvds_pll_config(struct stm_lvds *lvds, struct lvds_phy_info *phy)
{
unsigned int pll_in_khz, bdiv = 0, mdiv = 0, ndiv = 0;
struct clk_hw *hwclk;
int multiplier;
/*
* The LVDS PHY includes a low power low jitter high performance and
* highly configuration Phase Locked Loop supporting integer and
* fractional multiplication ratios and Spread Spectrum Clocking. In
* integer mode, the only software supported feature for now, the PLL is
* made of a pre-divider NDIV, a feedback multiplier MDIV, followed by
* several post-dividers, each one with a specific application.
*
* ,------. ,-----. ,-----.
* Fref --> | NDIV | -Fpdf-> | PFD | --> | VCO | --------> Fvco
* `------' ,-> | | `-----' |
* | `-----' |
* | ,------. |
* `-------- | MDIV | <-----'
* `------'
*
* From the output of the VCO, the clock can be optionally extracted on
* the RCC clock observer, with a divider TDIV, for testing purpose, or
* is passed through a programmable post-divider BDIV. Finally, the
* frequency can be divided further with two fixed dividers.
*
* ,--------.
* ,-----> | DP div | ----------------> Fdp
* ,------. | `--------'
* Fvco --> | BDIV | ------------------------------------> Fbit
* | `------' ,------. |
* `-------------> | TDIV | --.---------------------> ClkObs
* '------' | ,--------.
* `--> | LS div | ------> Fls
* '--------'
*
* The LS and DP clock dividers operate at a fixed ratio of 7 and 3.5
* respectively with regards to fbit. LS divider converts the bit clock
* to a pixel clock per lane per clock sample (Fls). This is useful
* when used to generate a dot clock for the display unit RGB output,
* and DP divider is.
*/
hwclk = __clk_get_hw(lvds->pllref_clk);
if (!hwclk)
return;
pll_in_khz = clk_hw_get_rate(hwclk) / 1000;
if (lvds_is_dual_link(lvds->link_type))
multiplier = 2;
else
multiplier = 1;
lvds_pll_get_params(lvds, pll_in_khz,
lvds->pixel_clock_rate * 7 / 1000 / multiplier,
&bdiv, &mdiv, &ndiv);
/* Set BDIV, MDIV and NDIV */
lvds_write(lvds, phy->base + phy->ofs.PLLCR2, ndiv << 16);
lvds_set(lvds, phy->base + phy->ofs.PLLCR2, bdiv);
lvds_write(lvds, phy->base + phy->ofs.PLLSDCR1, mdiv);
/* Hardcode TDIV as dynamic values are not yet implemented */
lvds_write(lvds, phy->base + phy->ofs.PLLTESTCR, TDIV << 16);
/*
* For now, PLL just needs to be in integer mode
* Fractional and spread spectrum clocking are not yet implemented
*
* PLL integer mode:
* - PMRY_PLL_TWG_STEP = PMRY_PLL_SD_INT_RATIO
* - EN_TWG = 0
* - EN_SD = 0
* - DOWN_SPREAD = 0
*
* PLL fractional mode:
* - EN_TWG = 0
* - EN_SD = 1
* - DOWN_SPREAD = 0
*
* Spread Spectrum Clocking
* - EN_TWG = 1
* - EN_SD = 1
*/
/* Disable TWG and SD */
lvds_clear(lvds, phy->base + phy->ofs.PLLCR1, PHY_PLLCR1_EN_TWG | PHY_PLLCR1_EN_SD);
/* Power up bias and PLL dividers */
lvds_set(lvds, phy->base + phy->ofs.DCR, PHY_DCR_POWER_OK);
lvds_set(lvds, phy->base + phy->ofs.CMCR1, PHY_CMCR_CM_EN_DL);
lvds_set(lvds, phy->base + phy->ofs.CMCR2, PHY_CMCR_CM_EN_DL4);
/* Set up voltage mode */
lvds_set(lvds, phy->base + phy->ofs.PLLCPCR, 0x1);
lvds_set(lvds, phy->base + phy->ofs.BCR3, PHY_BCR3_VM_EN_DL);
lvds_set(lvds, phy->base + phy->ofs.BCR1, PHY_BCR1_EN_BIAS_DL);
/* Enable digital datalanes */
lvds_set(lvds, phy->base + phy->ofs.CFGCR, PHY_CFGCR_EN_DIG_DL);
}
static int lvds_pixel_clk_enable(struct clk_hw *hw)
{
struct stm_lvds *lvds = container_of(hw, struct stm_lvds, lvds_ck_px);
struct drm_device *drm = lvds->lvds_bridge.dev;
struct lvds_phy_info *phy;
int ret;
ret = clk_prepare_enable(lvds->pclk);
if (ret) {
drm_err(drm, "Failed to enable lvds peripheral clk\n");
return ret;
}
ret = clk_prepare_enable(lvds->pllref_clk);
if (ret) {
drm_err(drm, "Failed to enable lvds reference clk\n");
clk_disable_unprepare(lvds->pclk);
return ret;
}
/* In case we are operating in dual link the second PHY is set before the primary PHY. */
if (lvds->secondary) {
phy = lvds->secondary;
/* Release LVDS PHY from reset mode */
lvds_set(lvds, phy->base + phy->ofs.GCR, PHY_GCR_DIV_RSTN | PHY_GCR_RSTZ);
lvds_pll_config(lvds, phy);
ret = lvds_pll_enable(lvds, phy);
if (ret) {
drm_err(drm, "Failed to enable secondary PHY PLL: %d\n", ret);
return ret;
}
}
if (lvds->primary) {
phy = lvds->primary;
/* Release LVDS PHY from reset mode */
lvds_set(lvds, phy->base + phy->ofs.GCR, PHY_GCR_DIV_RSTN | PHY_GCR_RSTZ);
lvds_pll_config(lvds, phy);
ret = lvds_pll_enable(lvds, phy);
if (ret) {
drm_err(drm, "Failed to enable primary PHY PLL: %d\n", ret);
return ret;
}
}
return 0;
}
static void lvds_pixel_clk_disable(struct clk_hw *hw)
{
struct stm_lvds *lvds = container_of(hw, struct stm_lvds, lvds_ck_px);
/*
* For each PHY:
* Disable DP, LS, BIT clock outputs
* Shutdown the PLL
* Assert LVDS PHY in reset mode
*/
if (lvds->primary) {
lvds_clear(lvds, lvds->primary->base + lvds->primary->ofs.GCR,
(PHY_GCR_DP_CLK_OUT | PHY_GCR_LS_CLK_OUT | PHY_GCR_BIT_CLK_OUT));
lvds_clear(lvds, lvds->primary->base + lvds->primary->ofs.PLLCR1,
PHY_PLLCR1_PLL_EN);
lvds_clear(lvds, lvds->primary->base + lvds->primary->ofs.GCR,
PHY_GCR_DIV_RSTN | PHY_GCR_RSTZ);
}
if (lvds->secondary) {
lvds_clear(lvds, lvds->secondary->base + lvds->secondary->ofs.GCR,
(PHY_GCR_DP_CLK_OUT | PHY_GCR_LS_CLK_OUT | PHY_GCR_BIT_CLK_OUT));
lvds_clear(lvds, lvds->secondary->base + lvds->secondary->ofs.PLLCR1,
PHY_PLLCR1_PLL_EN);
lvds_clear(lvds, lvds->secondary->base + lvds->secondary->ofs.GCR,
PHY_GCR_DIV_RSTN | PHY_GCR_RSTZ);
}
clk_disable_unprepare(lvds->pllref_clk);
clk_disable_unprepare(lvds->pclk);
}
static unsigned long lvds_pixel_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct stm_lvds *lvds = container_of(hw, struct stm_lvds, lvds_ck_px);
struct drm_device *drm = lvds->lvds_bridge.dev;
unsigned int pll_in_khz, bdiv, mdiv, ndiv;
int ret, multiplier, pll_out_khz;
u32 val;
ret = clk_prepare_enable(lvds->pclk);
if (ret) {
drm_err(drm, "Failed to enable lvds peripheral clk\n");
return 0;
}
if (lvds_is_dual_link(lvds->link_type))
multiplier = 2;
else
multiplier = 1;
val = lvds_read(lvds, lvds->primary->base + lvds->primary->ofs.PLLCR2);
ndiv = (val & PHY_PLLCR2_NDIV) >> 16;
bdiv = (val & PHY_PLLCR2_BDIV) >> 0;
mdiv = (unsigned int)lvds_read(lvds,
lvds->primary->base + lvds->primary->ofs.PLLSDCR1);
pll_in_khz = (unsigned int)(parent_rate / 1000);
/* Compute values if not yet accessible */
if (val == 0 || mdiv == 0) {
lvds_pll_get_params(lvds, pll_in_khz,
lvds->pixel_clock_rate * 7 / 1000 / multiplier,
&bdiv, &mdiv, &ndiv);
}
pll_out_khz = pll_get_clkout_khz(pll_in_khz, bdiv, mdiv, ndiv);
drm_dbg(drm, "ndiv %d , bdiv %d, mdiv %d, pll_out_khz %d\n",
ndiv, bdiv, mdiv, pll_out_khz);
/*
* 1/7 because for each pixel in 1 lane there is 7 bits
* We want pixclk, not bitclk
*/
lvds->pixel_clock_rate = pll_out_khz * 1000 * multiplier / 7;
clk_disable_unprepare(lvds->pclk);
return (unsigned long)lvds->pixel_clock_rate;
}
static long lvds_pixel_clk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct stm_lvds *lvds = container_of(hw, struct stm_lvds, lvds_ck_px);
unsigned int pll_in_khz, bdiv = 0, mdiv = 0, ndiv = 0;
const struct drm_connector *connector;
const struct drm_display_mode *mode;
int multiplier;
connector = &lvds->connector;
if (!connector)
return -EINVAL;
if (list_empty(&connector->modes)) {
drm_dbg(connector->dev, "connector: empty modes list\n");
return -EINVAL;
}
mode = list_first_entry(&connector->modes,
struct drm_display_mode, head);
pll_in_khz = (unsigned int)(*parent_rate / 1000);
if (lvds_is_dual_link(lvds->link_type))
multiplier = 2;
else
multiplier = 1;
lvds_pll_get_params(lvds, pll_in_khz, mode->clock * 7 / multiplier, &bdiv, &mdiv, &ndiv);
/*
* 1/7 because for each pixel in 1 lane there is 7 bits
* We want pixclk, not bitclk
*/
lvds->pixel_clock_rate = (unsigned long)pll_get_clkout_khz(pll_in_khz, bdiv, mdiv, ndiv)
* 1000 * multiplier / 7;
return lvds->pixel_clock_rate;
}
static const struct clk_ops lvds_pixel_clk_ops = {
.enable = lvds_pixel_clk_enable,
.disable = lvds_pixel_clk_disable,
.recalc_rate = lvds_pixel_clk_recalc_rate,
.round_rate = lvds_pixel_clk_round_rate,
};
static const struct clk_init_data clk_data = {
.name = "clk_pix_lvds",
.ops = &lvds_pixel_clk_ops,
.parent_names = (const char * []) {"ck_ker_lvdsphy"},
.num_parents = 1,
.flags = CLK_IGNORE_UNUSED,
};
static void lvds_pixel_clk_unregister(void *data)
{
struct stm_lvds *lvds = data;
of_clk_del_provider(lvds->dev->of_node);
clk_hw_unregister(&lvds->lvds_ck_px);
}
static int lvds_pixel_clk_register(struct stm_lvds *lvds)
{
struct device_node *node = lvds->dev->of_node;
int ret;
lvds->lvds_ck_px.init = &clk_data;
/* set the rate by default at 148500000 */
lvds->pixel_clock_rate = 148500000;
ret = clk_hw_register(lvds->dev, &lvds->lvds_ck_px);
if (ret)
return ret;
ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get,
&lvds->lvds_ck_px);
if (ret)
clk_hw_unregister(&lvds->lvds_ck_px);
return ret;
}
/*
* Host configuration related
*/
static void lvds_config_data_mapping(struct stm_lvds *lvds)
{
struct drm_device *drm = lvds->lvds_bridge.dev;
const struct drm_display_info *info;
enum lvds_pixel (*bitmap)[7];
u32 lvds_dmlcr, lvds_dmmcr;
int i;
info = &(&lvds->connector)->display_info;
if (!info->num_bus_formats || !info->bus_formats) {
drm_warn(drm, "No LVDS bus format reported\n");
return;
}
switch (info->bus_formats[0]) {
case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: /* VESA-RGB666 */
drm_warn(drm, "Pixel format with data mapping not yet supported.\n");
return;
case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: /* VESA-RGB888 */
bitmap = lvds_bitmap_vesa_rgb888;
break;
case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: /* JEIDA-RGB888 */
bitmap = lvds_bitmap_jeida_rgb888;
break;
default:
drm_warn(drm, "Unsupported LVDS bus format 0x%04x\n", info->bus_formats[0]);
return;
}
/* Set bitmap for each lane */
for (i = 0; i < 5; i++) {
lvds_dmlcr = ((bitmap[i][0])
+ (bitmap[i][1] << 5)
+ (bitmap[i][2] << 10)
+ (bitmap[i][3] << 15));
lvds_dmmcr = ((bitmap[i][4])
+ (bitmap[i][5] << 5)
+ (bitmap[i][6] << 10));
lvds_write(lvds, LVDS_DMLCR(i), lvds_dmlcr);
lvds_write(lvds, LVDS_DMMCR(i), lvds_dmmcr);
}
}
static void lvds_config_mode(struct stm_lvds *lvds)
{
u32 bus_flags, lvds_cr = 0, lvds_cdl1cr = 0, lvds_cdl2cr = 0;
const struct drm_display_mode *mode;
const struct drm_connector *connector;
connector = &lvds->connector;
if (!connector)
return;
if (list_empty(&connector->modes)) {
drm_dbg(connector->dev, "connector: empty modes list\n");
return;
}
bus_flags = connector->display_info.bus_flags;
mode = list_first_entry(&connector->modes,
struct drm_display_mode, head);
lvds_clear(lvds, LVDS_CR, CR_LKMOD);
lvds_clear(lvds, LVDS_CDL1CR, CDLCR_DISTR0 | CDLCR_DISTR1 | CDLCR_DISTR2 |
CDLCR_DISTR3 | CDLCR_DISTR4);
lvds_clear(lvds, LVDS_CDL2CR, CDLCR_DISTR0 | CDLCR_DISTR1 | CDLCR_DISTR2 |
CDLCR_DISTR3 | CDLCR_DISTR4);
/* Set channel distribution */
if (lvds->primary)
lvds_cdl1cr = CDL1CR_DEFAULT;
if (lvds->secondary) {
lvds_cr |= CR_LKMOD;
lvds_cdl2cr = CDL2CR_DEFAULT;
}
/* Set signal polarity */
if (bus_flags & DRM_BUS_FLAG_DE_LOW)
lvds_cr |= CR_DEPOL;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
lvds_cr |= CR_HSPOL;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
lvds_cr |= CR_VSPOL;
switch (lvds->link_type) {
case LVDS_DUAL_LINK_EVEN_ODD_PIXELS: /* LKPHA = 0 */
lvds_cr &= ~CR_LKPHA;
break;
case LVDS_DUAL_LINK_ODD_EVEN_PIXELS: /* LKPHA = 1 */
lvds_cr |= CR_LKPHA;
break;
default:
drm_notice(lvds->lvds_bridge.dev, "No phase precised, setting default\n");
lvds_cr &= ~CR_LKPHA;
break;
}
/* Write config to registers */
lvds_set(lvds, LVDS_CR, lvds_cr);
lvds_write(lvds, LVDS_CDL1CR, lvds_cdl1cr);
lvds_write(lvds, LVDS_CDL2CR, lvds_cdl2cr);
}
static int lvds_connector_get_modes(struct drm_connector *connector)
{
struct stm_lvds *lvds = connector_to_stm_lvds(connector);
return drm_panel_get_modes(lvds->panel, connector);
}
static int lvds_connector_atomic_check(struct drm_connector *connector,
struct drm_atomic_state *state)
{
const struct drm_display_mode *panel_mode;
struct drm_connector_state *conn_state;
struct drm_crtc_state *crtc_state;
conn_state = drm_atomic_get_new_connector_state(state, connector);
if (!conn_state)
return -EINVAL;
if (list_empty(&connector->modes)) {
drm_dbg(connector->dev, "connector: empty modes list\n");
return -EINVAL;
}
if (!conn_state->crtc)
return -EINVAL;
panel_mode = list_first_entry(&connector->modes,
struct drm_display_mode, head);
/* We're not allowed to modify the resolution. */
crtc_state = drm_atomic_get_crtc_state(state, conn_state->crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
if (crtc_state->mode.hdisplay != panel_mode->hdisplay ||
crtc_state->mode.vdisplay != panel_mode->vdisplay)
return -EINVAL;
/* The flat panel mode is fixed, just copy it to the adjusted mode. */
drm_mode_copy(&crtc_state->adjusted_mode, panel_mode);
return 0;
}
static const struct drm_connector_helper_funcs lvds_conn_helper_funcs = {
.get_modes = lvds_connector_get_modes,
.atomic_check = lvds_connector_atomic_check,
};
static const struct drm_connector_funcs lvds_conn_funcs = {
.reset = drm_atomic_helper_connector_reset,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int lvds_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct stm_lvds *lvds = bridge_to_stm_lvds(bridge);
struct drm_connector *connector = &lvds->connector;
struct drm_encoder *encoder = bridge->encoder;
int ret;
if (!bridge->encoder) {
drm_err(bridge->dev, "Parent encoder object not found\n");
return -ENODEV;
}
/* Set the encoder type as caller does not know it */
bridge->encoder->encoder_type = DRM_MODE_ENCODER_LVDS;
/* No cloning support */
bridge->encoder->possible_clones = 0;
/* If we have a next bridge just attach it. */
if (lvds->next_bridge)
return drm_bridge_attach(bridge->encoder, lvds->next_bridge,
bridge, flags);
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
drm_err(bridge->dev, "Fix bridge driver to make connector optional!");
return -EINVAL;
}
/* Otherwise if we have a panel, create a connector. */
if (!lvds->panel)
return 0;
ret = drm_connector_init(bridge->dev, connector,
&lvds_conn_funcs, DRM_MODE_CONNECTOR_LVDS);
if (ret < 0)
return ret;
drm_connector_helper_add(connector, &lvds_conn_helper_funcs);
ret = drm_connector_attach_encoder(connector, encoder);
return ret;
}
static void lvds_atomic_enable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{
struct drm_atomic_state *state = old_bridge_state->base.state;
struct stm_lvds *lvds = bridge_to_stm_lvds(bridge);
struct drm_connector_state *conn_state;
struct drm_connector *connector;
int ret;
ret = clk_prepare_enable(lvds->pclk);
if (ret) {
drm_err(bridge->dev, "Failed to enable lvds peripheral clk\n");
return;
}
connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
if (!connector)
return;
conn_state = drm_atomic_get_new_connector_state(state, connector);
if (!conn_state)
return;
lvds_config_mode(lvds);
/* Set Data Mapping */
lvds_config_data_mapping(lvds);
/* Turn the output on. */
lvds_set(lvds, LVDS_CR, CR_LVDSEN);
if (lvds->panel) {
drm_panel_prepare(lvds->panel);
drm_panel_enable(lvds->panel);
}
}
static void lvds_atomic_disable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{
struct stm_lvds *lvds = bridge_to_stm_lvds(bridge);
if (lvds->panel) {
drm_panel_disable(lvds->panel);
drm_panel_unprepare(lvds->panel);
}
/* Disable LVDS module */
lvds_clear(lvds, LVDS_CR, CR_LVDSEN);
clk_disable_unprepare(lvds->pclk);
}
static const struct drm_bridge_funcs lvds_bridge_funcs = {
.attach = lvds_attach,
.atomic_enable = lvds_atomic_enable,
.atomic_disable = lvds_atomic_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 lvds_probe(struct platform_device *pdev)
{
struct device_node *port1, *port2, *remote;
struct device *dev = &pdev->dev;
struct reset_control *rstc;
struct stm_lvds *lvds;
int ret, dual_link;
dev_dbg(dev, "Probing LVDS driver...\n");
lvds = devm_kzalloc(dev, sizeof(*lvds), GFP_KERNEL);
if (!lvds)
return -ENOMEM;
lvds->dev = dev;
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0,
&lvds->panel, &lvds->next_bridge);
if (ret) {
dev_err_probe(dev, ret, "Panel not found\n");
return ret;
}
lvds->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(lvds->base)) {
ret = PTR_ERR(lvds->base);
dev_err(dev, "Unable to get regs %d\n", ret);
return ret;
}
lvds->pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(lvds->pclk)) {
ret = PTR_ERR(lvds->pclk);
dev_err(dev, "Unable to get peripheral clock: %d\n", ret);
return ret;
}
ret = clk_prepare_enable(lvds->pclk);
if (ret) {
dev_err(dev, "%s: Failed to enable peripheral clk\n", __func__);
return ret;
}
rstc = devm_reset_control_get_exclusive(dev, NULL);
if (IS_ERR(rstc)) {
ret = PTR_ERR(rstc);
goto err_lvds_probe;
}
reset_control_assert(rstc);
usleep_range(10, 20);
reset_control_deassert(rstc);
port1 = of_graph_get_port_by_id(dev->of_node, 1);
port2 = of_graph_get_port_by_id(dev->of_node, 2);
dual_link = drm_of_lvds_get_dual_link_pixel_order(port1, port2);
switch (dual_link) {
case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS:
lvds->link_type = LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
lvds->primary = &lvds_phy_16ff_primary;
lvds->secondary = &lvds_phy_16ff_secondary;
break;
case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS:
lvds->link_type = LVDS_DUAL_LINK_EVEN_ODD_PIXELS;
lvds->primary = &lvds_phy_16ff_primary;
lvds->secondary = &lvds_phy_16ff_secondary;
break;
case -EINVAL:
/*
* drm_of_lvds_get_dual_pixel_order returns 4 possible values.
* In the case where the returned value is an error, it can be
* either ENODEV or EINVAL. Seeing the structure of this
* function, EINVAL means that either port1 or port2 is not
* present in the device tree.
* In that case, the lvds panel can be a single link panel, or
* there is a semantical error in the device tree code.
*/
remote = of_get_next_available_child(port1, NULL);
if (remote) {
if (of_graph_get_remote_endpoint(remote)) {
lvds->link_type = LVDS_SINGLE_LINK_PRIMARY;
lvds->primary = &lvds_phy_16ff_primary;
lvds->secondary = NULL;
} else {
ret = -EINVAL;
}
of_node_put(remote);
}
remote = of_get_next_available_child(port2, NULL);
if (remote) {
if (of_graph_get_remote_endpoint(remote)) {
lvds->link_type = LVDS_SINGLE_LINK_SECONDARY;
lvds->primary = NULL;
lvds->secondary = &lvds_phy_16ff_secondary;
} else {
ret = (ret == -EINVAL) ? -EINVAL : 0;
}
of_node_put(remote);
}
break;
default:
ret = -EINVAL;
goto err_lvds_probe;
}
of_node_put(port1);
of_node_put(port2);
lvds->pllref_clk = devm_clk_get(dev, "ref");
if (IS_ERR(lvds->pllref_clk)) {
ret = PTR_ERR(lvds->pllref_clk);
dev_err(dev, "Unable to get reference clock: %d\n", ret);
goto err_lvds_probe;
}
ret = lvds_pixel_clk_register(lvds);
if (ret) {
dev_err(dev, "Failed to register LVDS pixel clock: %d\n", ret);
goto err_lvds_probe;
}
lvds->lvds_bridge.funcs = &lvds_bridge_funcs;
lvds->lvds_bridge.of_node = dev->of_node;
lvds->hw_version = lvds_read(lvds, LVDS_VERR);
dev_info(dev, "version 0x%02x initialized\n", lvds->hw_version);
drm_bridge_add(&lvds->lvds_bridge);
platform_set_drvdata(pdev, lvds);
clk_disable_unprepare(lvds->pclk);
return 0;
err_lvds_probe:
clk_disable_unprepare(lvds->pclk);
return ret;
}
static int lvds_remove(struct platform_device *pdev)
{
struct stm_lvds *lvds = platform_get_drvdata(pdev);
lvds_pixel_clk_unregister(lvds);
drm_bridge_remove(&lvds->lvds_bridge);
return 0;
}
static const struct of_device_id lvds_dt_ids[] = {
{
.compatible = "st,stm32mp25-lvds",
.data = NULL
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, lvds_dt_ids);
static struct platform_driver lvds_platform_driver = {
.probe = lvds_probe,
.remove = lvds_remove,
.driver = {
.name = "stm32-display-lvds",
.owner = THIS_MODULE,
.of_match_table = lvds_dt_ids,
},
};
module_platform_driver(lvds_platform_driver);
MODULE_AUTHOR("Raphaël Gallais-Pou <raphael.gallais-pou@foss.st.com>");
MODULE_AUTHOR("Philippe Cornu <philippe.cornu@foss.st.com>");
MODULE_AUTHOR("Yannick Fertre <yannick.fertre@foss.st.com>");
MODULE_DESCRIPTION("STMicroelectronics LVDS Display Interface Transmitter DRM driver");
MODULE_LICENSE("GPL");
......@@ -992,6 +992,17 @@ static const struct drm_cmdline_tv_option_test drm_cmdline_tv_option_tests[] = {
TV_OPT_TEST(PAL_M, "720x480i,tv_mode=PAL-M", drm_mode_analog_ntsc_480i),
TV_OPT_TEST(PAL_N, "720x576i,tv_mode=PAL-N", drm_mode_analog_pal_576i),
TV_OPT_TEST(SECAM, "720x576i,tv_mode=SECAM", drm_mode_analog_pal_576i),
{
.name = "MONO_525",
.cmdline = "720x480i,tv_mode=Mono",
.mode_fn = drm_mode_analog_ntsc_480i,
.tv_mode = DRM_MODE_TV_MODE_MONOCHROME,
}, {
.name = "MONO_625",
.cmdline = "720x576i,tv_mode=Mono",
.mode_fn = drm_mode_analog_pal_576i,
.tv_mode = DRM_MODE_TV_MODE_MONOCHROME,
},
};
static void drm_cmdline_tv_option_desc(const struct drm_cmdline_tv_option_test *t,
......
......@@ -777,6 +777,7 @@ struct drm_get_tv_mode_from_name_test drm_get_tv_mode_from_name_valid_tests[] =
TV_MODE_NAME("PAL-M", DRM_MODE_TV_MODE_PAL_M),
TV_MODE_NAME("PAL-N", DRM_MODE_TV_MODE_PAL_N),
TV_MODE_NAME("SECAM", DRM_MODE_TV_MODE_SECAM),
TV_MODE_NAME("Mono", DRM_MODE_TV_MODE_MONOCHROME),
};
static void
......
......@@ -130,7 +130,38 @@ static void drm_test_modes_analog_tv_pal_576i_inlined(struct kunit *test)
KUNIT_EXPECT_TRUE(test, drm_mode_equal(expected, mode));
}
static void drm_test_modes_analog_tv_mono_576i(struct kunit *test)
{
struct drm_test_modes_priv *priv = test->priv;
struct drm_display_mode *mode;
mode = drm_analog_tv_mode(priv->drm,
DRM_MODE_TV_MODE_MONOCHROME,
13500 * HZ_PER_KHZ, 720, 576,
true);
KUNIT_ASSERT_NOT_NULL(test, mode);
KUNIT_EXPECT_EQ(test, drm_mode_vrefresh(mode), 50);
KUNIT_EXPECT_EQ(test, mode->hdisplay, 720);
/* BT.601 defines hsync_start at 732 for 576i */
KUNIT_EXPECT_EQ(test, mode->hsync_start, 732);
/*
* The PAL standard expects a line to take 64us. With a pixel
* clock of 13.5 MHz, a pixel takes around 74ns, so we need to
* have 64000ns / 74ns = 864.
*
* This is also mandated by BT.601.
*/
KUNIT_EXPECT_EQ(test, mode->htotal, 864);
KUNIT_EXPECT_EQ(test, mode->vdisplay, 576);
KUNIT_EXPECT_EQ(test, mode->vtotal, 625);
}
static struct kunit_case drm_modes_analog_tv_tests[] = {
KUNIT_CASE(drm_test_modes_analog_tv_mono_576i),
KUNIT_CASE(drm_test_modes_analog_tv_ntsc_480i),
KUNIT_CASE(drm_test_modes_analog_tv_ntsc_480i_inlined),
KUNIT_CASE(drm_test_modes_analog_tv_pal_576i),
......
......@@ -17,6 +17,8 @@ enum drm_dsc_params_type {
DRM_DSC_1_2_420,
};
struct drm_printer;
void drm_dsc_dp_pps_header_init(struct dp_sdp_header *pps_header);
int drm_dsc_dp_rc_buffer_size(u8 rc_buffer_block_size, u8 rc_buffer_size);
void drm_dsc_pps_payload_pack(struct drm_dsc_picture_parameter_set *pps_sdp,
......@@ -28,6 +30,7 @@ int drm_dsc_compute_rc_parameters(struct drm_dsc_config *vdsc_cfg);
u8 drm_dsc_initial_scale_value(const struct drm_dsc_config *dsc);
u32 drm_dsc_flatness_det_thresh(const struct drm_dsc_config *dsc);
u32 drm_dsc_get_bpp_int(const struct drm_dsc_config *vdsc_cfg);
void drm_dsc_dump_config(struct drm_printer *p, int indent, const struct drm_dsc_config *cfg);
#endif /* _DRM_DSC_HELPER_H_ */
......@@ -214,4 +214,27 @@ static inline s64 drm_fixp_exp(s64 x)
return sum;
}
static inline int fxp_q4_from_int(int val_int)
{
return val_int << 4;
}
static inline int fxp_q4_to_int(int val_q4)
{
return val_q4 >> 4;
}
static inline int fxp_q4_to_int_roundup(int val_q4)
{
return (val_q4 + 0xf) >> 4;
}
static inline int fxp_q4_to_frac(int val_q4)
{
return val_q4 & 0xf;
}
#define FXP_Q4_FMT "%d.%04d"
#define FXP_Q4_ARGS(val_q4) fxp_q4_to_int(val_q4), (fxp_q4_to_frac(val_q4) * 625)
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment