vout: starfive: Add driver for the StarFive JH7110 display subsystem

Add driver for the StarFive JH7110 display subsystem

Signed-off-by: Shengyang Chen <shengyang.chen@starfivetech.com>
Signed-off-by: Hal Feng <hal.feng@starfivetech.com>
This commit is contained in:
shengyang.chen 2023-06-12 15:31:38 +08:00 committed by Hal Feng
parent dce2b71254
commit c8bc7c051d
52 changed files with 17580 additions and 68 deletions

View file

@ -387,6 +387,8 @@ source "drivers/gpu/drm/solomon/Kconfig"
source "drivers/gpu/drm/sprd/Kconfig"
source "drivers/gpu/drm/verisilicon/Kconfig"
config DRM_HYPERV
tristate "DRM Support for Hyper-V synthetic video device"
depends on DRM && PCI && MMU && HYPERV

View file

@ -198,3 +198,4 @@ obj-$(CONFIG_DRM_HYPERV) += hyperv/
obj-y += solomon/
obj-$(CONFIG_DRM_SPRD) += sprd/
obj-$(CONFIG_DRM_LOONGSON) += loongson/
obj-$(CONFIG_DRM_VERISILICON) += verisilicon/

View file

@ -1127,6 +1127,42 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
struct drm_crtc_state *old_crtc_state, *new_crtc_state;
int i;
for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
const struct drm_crtc_helper_funcs *funcs;
int ret;
/* Shut down everything that needs a full modeset. */
if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
continue;
if (!crtc_needs_disable(old_crtc_state, new_crtc_state))
continue;
funcs = crtc->helper_private;
drm_dbg_atomic(dev, "disabling [CRTC:%d:%s]\n",
crtc->base.id, crtc->name);
/* Right function depends upon target state. */
if (new_crtc_state->enable && funcs->prepare)
funcs->prepare(crtc);
else if (funcs->atomic_disable)
funcs->atomic_disable(crtc, old_state);
else if (funcs->disable)
funcs->disable(crtc);
else if (funcs->dpms)
funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
if (!drm_dev_has_vblank(dev))
continue;
ret = drm_crtc_vblank_get(crtc);
WARN_ONCE(ret != -EINVAL, "driver forgot to call drm_crtc_vblank_off()\n");
if (ret == 0)
drm_crtc_vblank_put(crtc);
}
for_each_oldnew_connector_in_state(old_state, connector, old_conn_state, new_conn_state, i) {
const struct drm_encoder_helper_funcs *funcs;
struct drm_encoder *encoder;
@ -1186,51 +1222,6 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
drm_atomic_bridge_chain_post_disable(bridge, old_state);
}
for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
const struct drm_crtc_helper_funcs *funcs;
int ret;
/* Shut down everything that needs a full modeset. */
if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
continue;
if (!crtc_needs_disable(old_crtc_state, new_crtc_state))
continue;
funcs = crtc->helper_private;
drm_dbg_atomic(dev, "disabling [CRTC:%d:%s]\n",
crtc->base.id, crtc->name);
/* Right function depends upon target state. */
if (new_crtc_state->enable && funcs->prepare)
funcs->prepare(crtc);
else if (funcs->atomic_disable)
funcs->atomic_disable(crtc, old_state);
else if (funcs->disable)
funcs->disable(crtc);
else if (funcs->dpms)
funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
if (!drm_dev_has_vblank(dev))
continue;
ret = drm_crtc_vblank_get(crtc);
/*
* Self-refresh is not a true "disable"; ensure vblank remains
* enabled.
*/
if (new_crtc_state->self_refresh_active)
WARN_ONCE(ret != 0,
"driver disabled vblank in self-refresh\n");
else
WARN_ONCE(ret != -EINVAL,
"driver forgot to call drm_crtc_vblank_off()\n");
if (ret == 0)
drm_crtc_vblank_put(crtc);
}
}
/**
@ -1466,27 +1457,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
struct drm_connector_state *new_conn_state;
int i;
for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
const struct drm_crtc_helper_funcs *funcs;
/* Need to filter out CRTCs where only planes change. */
if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
continue;
if (!new_crtc_state->active)
continue;
funcs = crtc->helper_private;
if (new_crtc_state->enable) {
drm_dbg_atomic(dev, "enabling [CRTC:%d:%s]\n",
crtc->base.id, crtc->name);
if (funcs->atomic_enable)
funcs->atomic_enable(crtc, old_state);
else if (funcs->commit)
funcs->commit(crtc);
}
}
for_each_new_connector_in_state(old_state, connector, new_conn_state, i) {
const struct drm_encoder_helper_funcs *funcs;
@ -1525,6 +1496,28 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
drm_atomic_bridge_chain_enable(bridge, old_state);
}
for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
const struct drm_crtc_helper_funcs *funcs;
/* Need to filter out CRTCs where only planes change. */
if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
continue;
if (!new_crtc_state->active)
continue;
funcs = crtc->helper_private;
if (new_crtc_state->enable) {
drm_dbg_atomic(dev, "enabling [CRTC:%d:%s]\n",
crtc->base.id, crtc->name);
if (funcs->atomic_enable)
funcs->atomic_enable(crtc, old_state);
else if (funcs->commit)
funcs->commit(crtc);
}
}
drm_atomic_helper_commit_writebacks(dev, old_state);
}
EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables);
@ -1674,7 +1667,7 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
ret = wait_event_timeout(dev->vblank[i].queue,
old_state->crtcs[i].last_vblank_count !=
drm_crtc_vblank_count(crtc),
msecs_to_jiffies(100));
msecs_to_jiffies(500));
WARN(!ret, "[CRTC:%d:%s] vblank wait timed out\n",
crtc->base.id, crtc->name);

View file

@ -6,5 +6,5 @@ sil164-y := sil164_drv.o
obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o
tda998x-y := tda998x_drv.o
obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o tda998x_pin.o
obj-$(CONFIG_DRM_I2C_NXP_TDA9950) += tda9950.o

View file

@ -1830,9 +1830,15 @@ static int tda998x_create(struct device *dev)
timer_setup(&priv->edid_delay_timer, tda998x_edid_delay_done, 0);
INIT_WORK(&priv->detect_work, tda998x_detect_work);
#if 0
priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3);
priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1);
priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5);
#else
priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3);
priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(4) | VIP_CNTRL_1_SWAP_D(5);
priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(0) | VIP_CNTRL_2_SWAP_F(1);
#endif
/* CEC I2C address bound to TDA998x I2C addr by configuration pins */
priv->cec_addr = 0x34 + (client->addr & 0x03);

View file

@ -0,0 +1,40 @@
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/iommu.h>
#include <drm/drm_drv.h>
#include <drm/drm_of.h>
#define DRIVER_NAME "starfive"
#define DRIVER_DESC "StarFive Soc DRM"
#define DRIVER_DATE "20220624"
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
#define DRIVER_VERSION "v1.0.0"
static int starfive_drm_platform_probe(struct platform_device *pdev)
{
dev_info(&pdev->dev, "%s, ok\n", __func__);
return 0;
}
static const struct of_device_id tda998x_rgb_dt_ids[] = {
{ .compatible = "starfive,tda998x_rgb_pin", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, starfive_drm_dt_ids);
static struct platform_driver starfive_drm_platform_driver = {
.probe = starfive_drm_platform_probe,
.driver = {
.name = "tda998x_rgb_dt_ids",
.of_match_table = tda998x_rgb_dt_ids,
},
};
module_platform_driver(starfive_drm_platform_driver);
MODULE_AUTHOR("David Li <david.li@starfivetech.com>");
MODULE_DESCRIPTION("starfive DRM Driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,57 @@
# SPDX-License-Identifier: GPL-2.0
config DRM_VERISILICON
tristate "DRM Support for VeriSilicon"
depends on DRM
select DRM_KMS_HELPER
help
Choose this option if you have a VeriSilicon soc chipset.
This driver provides VeriSilicon kernel mode
setting and buffer management. It does not
provide 2D or 3D acceleration.
config VERISILICON_VIRTUAL_DISPLAY
bool "display content output to debugfs file"
depends on DRM_VERISILICON
select DEBUG_FS
help
This is a debug feature which capture video content output
from display controller. Output path is debugfs/dri/connector/.
The content format is ARGB which Alpha is 0 for 8bits.
Disabled in default.
config VERISILICON_MMU
bool "MMU support for VeriSilicon display controller"
depends on DRM_VERISILICON
help
This is a memory management function which translate virtual address
to physical address. DPU MMU only do address translate, doesn't
support shareable.
config VERISILICON_DEC
bool "DEC support for VeriSilicon display controller"
depends on DRM_VERISILICON
help
This is a decompression function which reads compressed pixels from
external memory (DDR or SRAM) under DPU's control, then it decompress
comprssed pixels before returing these pixels to DPU.
config STARFIVE_INNO_HDMI
bool "HDMI2.0"
help
This selects support for Rockchip SoC specific extensions
for the Innosilicon HDMI driver. If you want to enable
HDMI on RK3036 based SoC, you should select this option.
config STARFIVE_DSI
bool "Starfive MIPI DSI Select"
depends on DRM_VERISILICON
select DRM_MIPI_DSI
select GENERIC_PHY
select GENERIC_PHY_MIPI_DPHY
help
This selects support for starfive SoC specific extensions
for the Synopsys DesignWare MIPI driver. If you want to
enable MIPI DSI on VIC7110 based SoC, you should
select this option.
source "drivers/gpu/drm/verisilicon/adv7511/Kconfig"

View file

@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-2.0
vs_drm-objs := vs_dc_hw.o \
vs_dc.o \
vs_crtc.o \
vs_drv.o \
vs_fb.o \
vs_gem.o \
vs_plane.o \
vs_simple_enc.o
vs_drm-$(CONFIG_VERISILICON_VIRTUAL_DISPLAY) += vs_virtual.o
vs_drm-$(CONFIG_VERISILICON_MMU) += vs_dc_mmu.o
vs_drm-$(CONFIG_VERISILICON_DEC) += vs_dc_dec.o
vs_drm-$(CONFIG_STARFIVE_INNO_HDMI) += inno_hdmi.o \
starfive_hdmi_audio.o
vs_drm-$(CONFIG_STARFIVE_DSI) += starfive_drm_dsi.o starfive_drm_seeedpanel.o
obj-$(CONFIG_DRM_VERISILICON) += vs_drm.o

View file

@ -0,0 +1,118 @@
build drm need to config:
# CONFIG_DRM_I2C_ADV7511_CEC is not set
CONFIG_DRM_VERISILICON=y
#CONFIG_DRM_I2C_ADV7513 is not set
CONFIG_STARFIVE_INNO_HDMI=y
CONFIG_DRM_LEGACY=y
# CONFIG_DRM_VGEM is not set
# CONFIG_DRM_VKMS is not set
notes:
1、DC8200 rgbpad GPIO 和其他模块存在大规模冲突, 需要等evb板子做进一步确认
2、evb板子当前与hdmi冲突的gpio有
i2c1 sdio1 tdm
调试hdmi的时候建议在jh7100-common.dtsi文件
将这些模块的pin的结点进行注释或者将node disabled
===============================================================================================
build drm mipi-dsi need to config:
# CONFIG_DRM_I2C_ADV7511_CEC is not set
CONFIG_DRM_VERISILICON=y
#CONFIG_DRM_I2C_ADV7513 is not set
CONFIG_DRM_LEGACY=y
CONFIG_STARFIVE_DSI=y
CONFIG_PHY_M31_DPHY_RX0=y
#STARFIVE_INNO_HDMI is not set
#CONFIG_DRM_I2C_NXP_TDA998X is not set
# CONFIG_DRM_VGEM is not set
# CONFIG_DRM_VKMS is not set
notes:
1.
测试mipi-dsi时,在编译前需要打开的dts节点有:
jh7110.dtsi:
dc8200, encoder: display-encoder, mipi_dphy, mipi_dsi, mipi_panel
jh7100-common.dtsi:
&i2c2
2.
evb板子当前与mipi-dsi冲突的gpio有:
sdio1(mmc1)
调试mipi-dsi的时候建议在jh7100-common.dtsi文件
将这些模块的pin的结点进行注释或者将node disabled
3.
mipi-dsi通路目前和rgb2hdmi通路是互斥的,
不能同时打开。使用某一个时需要关闭另一个
如:
使用mipi-dsi需要关闭
CONFIG_DRM_I2C_NXP_TDA998X
===============================================================================================
build rgb2hdmi channel need to config:
CONFIG_DRM=y
CONFIG_DRM_I2C_NXP_TDA998X=y
CONFIG_DRM_VERISILICON=y
CONFIG_STARFIVE_INNO_HDMI=y
# CONFIG_DRM_IMG_NULLDISP is not set
CONFIG_DRM_LEGACY=y
# CONFIG_STARFIVE_DSI is not set
notes:
1.
evb板子当前与tda998x-rgb2hdmi通路存在gpio冲突的模块有
pdm0, i2srx_3ch, pwmdac, spi0~spi6, inno_hdmi, tdm, i2c0, can1, ptc, vin_sysctl, pcie0
调试tda998x-rgb2hdmi通路的时候建议在jh7110-common.dtsi文件
将这些模块的pin的结点进行注释或者将node disabled(&hdmi节点比较特殊,详见3)
2.
mipi-dsi通路目前和rgb2hdmi通路是互斥的
不能同时打开。使用某一个时需要关闭另一个
如:
使用rgb2hdmi需要关闭
CONFIG_STARFIVE_DSI
3.
关于&hdmi节点
在使用rgb2hdmi时需要将其打开(status = "okay";)
并将其引用的pin(pinctrl-0 = <&inno_hdmi_pins>;) 注释掉
如:
&hdmi {
status = "okay";//okay //rgb need this, connector/encoder problem
//pinctrl-names = "default"; //if rgb, comment them
//pinctrl-0 = <&inno_hdmi_pins>; //if rgb, comment them
hdmi_in: port {
#address-cells = <1>;
#size-cells = <0>;
hdmi_in_lcdc: endpoint@0 {
reg = <0>;
remote-endpoint = <&dc_out_dpi1>;
};
};
};
4.
测试rgb2hdmi需要打开的dts节点有:
jh7110.dtsi:
hdmi_output, hdmi, dc8200
jh7110-common.dtsi:
&hdmi_output, &hdmi(详见3), &dc8200, &i2c2
关闭节点:
jh7100-common.dtsi:
&encoder

View file

@ -0,0 +1,25 @@
# SPDX-License-Identifier: GPL-2.0-only
config DRM_I2C_ADV7513
tristate "ADV7513 encoder"
depends on OF
select DRM_KMS_HELPER
select REGMAP_I2C
select DRM_MIPI_DSI
help
Support for the Analog Devices ADV7511(W)/13/33/35 HDMI encoders.
config DRM_I2C_ADV7511_AUDIO
bool "ADV7511 HDMI Audio driver"
depends on DRM_I2C_ADV7511 && SND_SOC
select SND_SOC_HDMI_CODEC
help
Support the ADV7511 HDMI Audio interface. This is used in
conjunction with the AV7511 HDMI driver.
config DRM_I2C_ADV7511_CEC
bool "ADV7511/33/35 HDMI CEC driver"
depends on DRM_I2C_ADV7513
select CEC_CORE
default y
help
When selected the HDMI transmitter will support the CEC feature.

View file

@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
adv7513-y := adv7511_drv.o adv7533.o
adv7511-$(CONFIG_DRM_I2C_ADV7511_AUDIO) += adv7511_audio.o
adv7511-$(CONFIG_DRM_I2C_ADV7511_CEC) += adv7511_cec.o
obj-y += adv7513.o

View file

@ -0,0 +1,420 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Analog Devices ADV7511 HDMI transmitter driver
*
* Copyright 2012 Analog Devices Inc.
*/
#ifndef __DRM_I2C_ADV7511_H__
#define __DRM_I2C_ADV7511_H__
#include <linux/hdmi.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <drm/drm_bridge.h>
#include <drm/drm_connector.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#define ADV7511_REG_CHIP_REVISION 0x00
#define ADV7511_REG_N0 0x01
#define ADV7511_REG_N1 0x02
#define ADV7511_REG_N2 0x03
#define ADV7511_REG_SPDIF_FREQ 0x04
#define ADV7511_REG_CTS_AUTOMATIC1 0x05
#define ADV7511_REG_CTS_AUTOMATIC2 0x06
#define ADV7511_REG_CTS_MANUAL0 0x07
#define ADV7511_REG_CTS_MANUAL1 0x08
#define ADV7511_REG_CTS_MANUAL2 0x09
#define ADV7511_REG_AUDIO_SOURCE 0x0a
#define ADV7511_REG_AUDIO_CONFIG 0x0b
#define ADV7511_REG_I2S_CONFIG 0x0c
#define ADV7511_REG_I2S_WIDTH 0x0d
#define ADV7511_REG_AUDIO_SUB_SRC0 0x0e
#define ADV7511_REG_AUDIO_SUB_SRC1 0x0f
#define ADV7511_REG_AUDIO_SUB_SRC2 0x10
#define ADV7511_REG_AUDIO_SUB_SRC3 0x11
#define ADV7511_REG_AUDIO_CFG1 0x12
#define ADV7511_REG_AUDIO_CFG2 0x13
#define ADV7511_REG_AUDIO_CFG3 0x14
#define ADV7511_REG_I2C_FREQ_ID_CFG 0x15
#define ADV7511_REG_VIDEO_INPUT_CFG1 0x16
#define ADV7511_REG_CSC_UPPER(x) (0x18 + (x) * 2)
#define ADV7511_REG_CSC_LOWER(x) (0x19 + (x) * 2)
#define ADV7511_REG_SYNC_DECODER(x) (0x30 + (x))
#define ADV7511_REG_DE_GENERATOR (0x35 + (x))
#define ADV7511_REG_PIXEL_REPETITION 0x3b
#define ADV7511_REG_VIC_MANUAL 0x3c
#define ADV7511_REG_VIC_SEND 0x3d
#define ADV7511_REG_VIC_DETECTED 0x3e
#define ADV7511_REG_AUX_VIC_DETECTED 0x3f
#define ADV7511_REG_PACKET_ENABLE0 0x40
#define ADV7511_REG_POWER 0x41
#define ADV7511_REG_STATUS 0x42
#define ADV7511_REG_EDID_I2C_ADDR 0x43
#define ADV7511_REG_PACKET_ENABLE1 0x44
#define ADV7511_REG_PACKET_I2C_ADDR 0x45
#define ADV7511_REG_DSD_ENABLE 0x46
#define ADV7511_REG_VIDEO_INPUT_CFG2 0x48
#define ADV7511_REG_INFOFRAME_UPDATE 0x4a
#define ADV7511_REG_GC(x) (0x4b + (x)) /* 0x4b - 0x51 */
#define ADV7511_REG_AVI_INFOFRAME_VERSION 0x52
#define ADV7511_REG_AVI_INFOFRAME_LENGTH 0x53
#define ADV7511_REG_AVI_INFOFRAME_CHECKSUM 0x54
#define ADV7511_REG_AVI_INFOFRAME(x) (0x55 + (x)) /* 0x55 - 0x6f */
#define ADV7511_REG_AUDIO_INFOFRAME_VERSION 0x70
#define ADV7511_REG_AUDIO_INFOFRAME_LENGTH 0x71
#define ADV7511_REG_AUDIO_INFOFRAME_CHECKSUM 0x72
#define ADV7511_REG_AUDIO_INFOFRAME(x) (0x73 + (x)) /* 0x73 - 0x7c */
#define ADV7511_REG_INT_ENABLE(x) (0x94 + (x))
#define ADV7511_REG_INT(x) (0x96 + (x))
#define ADV7511_REG_INPUT_CLK_DIV 0x9d
#define ADV7511_REG_PLL_STATUS 0x9e
#define ADV7511_REG_HDMI_POWER 0xa1
#define ADV7511_REG_HDCP_HDMI_CFG 0xaf
#define ADV7511_REG_AN(x) (0xb0 + (x)) /* 0xb0 - 0xb7 */
#define ADV7511_REG_HDCP_STATUS 0xb8
#define ADV7511_REG_BCAPS 0xbe
#define ADV7511_REG_BKSV(x) (0xc0 + (x)) /* 0xc0 - 0xc3 */
#define ADV7511_REG_EDID_SEGMENT 0xc4
#define ADV7511_REG_DDC_STATUS 0xc8
#define ADV7511_REG_EDID_READ_CTRL 0xc9
#define ADV7511_REG_BSTATUS(x) (0xca + (x)) /* 0xca - 0xcb */
#define ADV7511_REG_TIMING_GEN_SEQ 0xd0
#define ADV7511_REG_POWER2 0xd6
#define ADV7511_REG_HSYNC_PLACEMENT_MSB 0xfa
#define ADV7511_REG_SYNC_ADJUSTMENT(x) (0xd7 + (x)) /* 0xd7 - 0xdc */
#define ADV7511_REG_TMDS_CLOCK_INV 0xde
#define ADV7511_REG_ARC_CTRL 0xdf
#define ADV7511_REG_CEC_I2C_ADDR 0xe1
#define ADV7511_REG_CEC_CTRL 0xe2
#define ADV7511_REG_CHIP_ID_HIGH 0xf5
#define ADV7511_REG_CHIP_ID_LOW 0xf6
/* Hardware defined default addresses for I2C register maps */
#define ADV7511_CEC_I2C_ADDR_DEFAULT 0x3c
#define ADV7511_EDID_I2C_ADDR_DEFAULT 0x3f
#define ADV7511_PACKET_I2C_ADDR_DEFAULT 0x38
#define ADV7511_CSC_ENABLE BIT(7)
#define ADV7511_CSC_UPDATE_MODE BIT(5)
#define ADV7511_INT0_HPD BIT(7)
#define ADV7511_INT0_VSYNC BIT(5)
#define ADV7511_INT0_AUDIO_FIFO_FULL BIT(4)
#define ADV7511_INT0_EDID_READY BIT(2)
#define ADV7511_INT0_HDCP_AUTHENTICATED BIT(1)
#define ADV7511_INT1_DDC_ERROR BIT(7)
#define ADV7511_INT1_BKSV BIT(6)
#define ADV7511_INT1_CEC_TX_READY BIT(5)
#define ADV7511_INT1_CEC_TX_ARBIT_LOST BIT(4)
#define ADV7511_INT1_CEC_TX_RETRY_TIMEOUT BIT(3)
#define ADV7511_INT1_CEC_RX_READY3 BIT(2)
#define ADV7511_INT1_CEC_RX_READY2 BIT(1)
#define ADV7511_INT1_CEC_RX_READY1 BIT(0)
#define ADV7511_ARC_CTRL_POWER_DOWN BIT(0)
#define ADV7511_CEC_CTRL_POWER_DOWN BIT(0)
#define ADV7511_POWER_POWER_DOWN BIT(6)
#define ADV7511_HDMI_CFG_MODE_MASK 0x2
#define ADV7511_HDMI_CFG_MODE_DVI 0x0
#define ADV7511_HDMI_CFG_MODE_HDMI 0x2
#define ADV7511_AUDIO_SELECT_I2C 0x0
#define ADV7511_AUDIO_SELECT_SPDIF 0x1
#define ADV7511_AUDIO_SELECT_DSD 0x2
#define ADV7511_AUDIO_SELECT_HBR 0x3
#define ADV7511_AUDIO_SELECT_DST 0x4
#define ADV7511_I2S_SAMPLE_LEN_16 0x2
#define ADV7511_I2S_SAMPLE_LEN_20 0x3
#define ADV7511_I2S_SAMPLE_LEN_18 0x4
#define ADV7511_I2S_SAMPLE_LEN_22 0x5
#define ADV7511_I2S_SAMPLE_LEN_19 0x8
#define ADV7511_I2S_SAMPLE_LEN_23 0x9
#define ADV7511_I2S_SAMPLE_LEN_24 0xb
#define ADV7511_I2S_SAMPLE_LEN_17 0xc
#define ADV7511_I2S_SAMPLE_LEN_21 0xd
#define ADV7511_SAMPLE_FREQ_44100 0x0
#define ADV7511_SAMPLE_FREQ_48000 0x2
#define ADV7511_SAMPLE_FREQ_32000 0x3
#define ADV7511_SAMPLE_FREQ_88200 0x8
#define ADV7511_SAMPLE_FREQ_96000 0xa
#define ADV7511_SAMPLE_FREQ_176400 0xc
#define ADV7511_SAMPLE_FREQ_192000 0xe
#define ADV7511_STATUS_POWER_DOWN_POLARITY BIT(7)
#define ADV7511_STATUS_HPD BIT(6)
#define ADV7511_STATUS_MONITOR_SENSE BIT(5)
#define ADV7511_STATUS_I2S_32BIT_MODE BIT(3)
#define ADV7511_PACKET_ENABLE_N_CTS BIT(8+6)
#define ADV7511_PACKET_ENABLE_AUDIO_SAMPLE BIT(8+5)
#define ADV7511_PACKET_ENABLE_AVI_INFOFRAME BIT(8+4)
#define ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME BIT(8+3)
#define ADV7511_PACKET_ENABLE_GC BIT(7)
#define ADV7511_PACKET_ENABLE_SPD BIT(6)
#define ADV7511_PACKET_ENABLE_MPEG BIT(5)
#define ADV7511_PACKET_ENABLE_ACP BIT(4)
#define ADV7511_PACKET_ENABLE_ISRC BIT(3)
#define ADV7511_PACKET_ENABLE_GM BIT(2)
#define ADV7511_PACKET_ENABLE_SPARE2 BIT(1)
#define ADV7511_PACKET_ENABLE_SPARE1 BIT(0)
#define ADV7511_REG_POWER2_HPD_SRC_MASK 0xc0
#define ADV7511_REG_POWER2_HPD_SRC_BOTH 0x00
#define ADV7511_REG_POWER2_HPD_SRC_HPD 0x40
#define ADV7511_REG_POWER2_HPD_SRC_CEC 0x80
#define ADV7511_REG_POWER2_HPD_SRC_NONE 0xc0
#define ADV7511_REG_POWER2_TDMS_ENABLE BIT(4)
#define ADV7511_REG_POWER2_GATE_INPUT_CLK BIT(0)
#define ADV7511_LOW_REFRESH_RATE_NONE 0x0
#define ADV7511_LOW_REFRESH_RATE_24HZ 0x1
#define ADV7511_LOW_REFRESH_RATE_25HZ 0x2
#define ADV7511_LOW_REFRESH_RATE_30HZ 0x3
#define ADV7511_AUDIO_CFG3_LEN_MASK 0x0f
#define ADV7511_I2C_FREQ_ID_CFG_RATE_MASK 0xf0
#define ADV7511_AUDIO_SOURCE_I2S 0
#define ADV7511_AUDIO_SOURCE_SPDIF 1
#define ADV7511_I2S_FORMAT_I2S 0
#define ADV7511_I2S_FORMAT_RIGHT_J 1
#define ADV7511_I2S_FORMAT_LEFT_J 2
#define ADV7511_PACKET(p, x) ((p) * 0x20 + (x))
#define ADV7511_PACKET_SDP(x) ADV7511_PACKET(0, x)
#define ADV7511_PACKET_MPEG(x) ADV7511_PACKET(1, x)
#define ADV7511_PACKET_ACP(x) ADV7511_PACKET(2, x)
#define ADV7511_PACKET_ISRC1(x) ADV7511_PACKET(3, x)
#define ADV7511_PACKET_ISRC2(x) ADV7511_PACKET(4, x)
#define ADV7511_PACKET_GM(x) ADV7511_PACKET(5, x)
#define ADV7511_PACKET_SPARE(x) ADV7511_PACKET(6, x)
#define ADV7511_REG_CEC_TX_FRAME_HDR 0x00
#define ADV7511_REG_CEC_TX_FRAME_DATA0 0x01
#define ADV7511_REG_CEC_TX_FRAME_LEN 0x10
#define ADV7511_REG_CEC_TX_ENABLE 0x11
#define ADV7511_REG_CEC_TX_RETRY 0x12
#define ADV7511_REG_CEC_TX_LOW_DRV_CNT 0x14
#define ADV7511_REG_CEC_RX_FRAME_HDR 0x15
#define ADV7511_REG_CEC_RX_FRAME_DATA0 0x16
#define ADV7511_REG_CEC_RX_FRAME_LEN 0x25
#define ADV7511_REG_CEC_RX_ENABLE 0x26
#define ADV7511_REG_CEC_RX_BUFFERS 0x4a
#define ADV7511_REG_CEC_LOG_ADDR_MASK 0x4b
#define ADV7511_REG_CEC_LOG_ADDR_0_1 0x4c
#define ADV7511_REG_CEC_LOG_ADDR_2 0x4d
#define ADV7511_REG_CEC_CLK_DIV 0x4e
#define ADV7511_REG_CEC_SOFT_RESET 0x50
#define ADV7533_REG_CEC_OFFSET 0x70
enum adv7511_input_clock {
ADV7511_INPUT_CLOCK_1X,
ADV7511_INPUT_CLOCK_2X,
ADV7511_INPUT_CLOCK_DDR,
};
enum adv7511_input_justification {
ADV7511_INPUT_JUSTIFICATION_EVENLY = 0,
ADV7511_INPUT_JUSTIFICATION_RIGHT = 1,
ADV7511_INPUT_JUSTIFICATION_LEFT = 2,
};
enum adv7511_input_sync_pulse {
ADV7511_INPUT_SYNC_PULSE_DE = 0,
ADV7511_INPUT_SYNC_PULSE_HSYNC = 1,
ADV7511_INPUT_SYNC_PULSE_VSYNC = 2,
ADV7511_INPUT_SYNC_PULSE_NONE = 3,
};
/**
* enum adv7511_sync_polarity - Polarity for the input sync signals
* @ADV7511_SYNC_POLARITY_PASSTHROUGH: Sync polarity matches that of
* the currently configured mode.
* @ADV7511_SYNC_POLARITY_LOW: Sync polarity is low
* @ADV7511_SYNC_POLARITY_HIGH: Sync polarity is high
*
* If the polarity is set to either LOW or HIGH the driver will configure the
* ADV7511 to internally invert the sync signal if required to match the sync
* polarity setting for the currently selected output mode.
*
* If the polarity is set to PASSTHROUGH, the ADV7511 will route the signal
* unchanged. This is used when the upstream graphics core already generates
* the sync signals with the correct polarity.
*/
enum adv7511_sync_polarity {
ADV7511_SYNC_POLARITY_PASSTHROUGH,
ADV7511_SYNC_POLARITY_LOW,
ADV7511_SYNC_POLARITY_HIGH,
};
/**
* struct adv7511_link_config - Describes adv7511 hardware configuration
* @input_color_depth: Number of bits per color component (8, 10 or 12)
* @input_colorspace: The input colorspace (RGB, YUV444, YUV422)
* @input_clock: The input video clock style (1x, 2x, DDR)
* @input_style: The input component arrangement variant
* @input_justification: Video input format bit justification
* @clock_delay: Clock delay for the input clock (in ps)
* @embedded_sync: Video input uses BT.656-style embedded sync
* @sync_pulse: Select the sync pulse
* @vsync_polarity: vsync input signal configuration
* @hsync_polarity: hsync input signal configuration
*/
struct adv7511_link_config {
unsigned int input_color_depth;
enum hdmi_colorspace input_colorspace;
enum adv7511_input_clock input_clock;
unsigned int input_style;
enum adv7511_input_justification input_justification;
int clock_delay;
bool embedded_sync;
enum adv7511_input_sync_pulse sync_pulse;
enum adv7511_sync_polarity vsync_polarity;
enum adv7511_sync_polarity hsync_polarity;
};
/**
* enum adv7511_csc_scaling - Scaling factor for the ADV7511 CSC
* @ADV7511_CSC_SCALING_1: CSC results are not scaled
* @ADV7511_CSC_SCALING_2: CSC results are scaled by a factor of two
* @ADV7511_CSC_SCALING_4: CSC results are scalled by a factor of four
*/
enum adv7511_csc_scaling {
ADV7511_CSC_SCALING_1 = 0,
ADV7511_CSC_SCALING_2 = 1,
ADV7511_CSC_SCALING_4 = 2,
};
/**
* struct adv7511_video_config - Describes adv7511 hardware configuration
* @csc_enable: Whether to enable color space conversion
* @csc_scaling_factor: Color space conversion scaling factor
* @csc_coefficents: Color space conversion coefficents
* @hdmi_mode: Whether to use HDMI or DVI output mode
* @avi_infoframe: HDMI infoframe
*/
struct adv7511_video_config {
bool csc_enable;
enum adv7511_csc_scaling csc_scaling_factor;
const uint16_t *csc_coefficents;
bool hdmi_mode;
struct hdmi_avi_infoframe avi_infoframe;
};
enum adv7511_type {
ADV7511,
ADV7533,
ADV7535,
};
#define ADV7511_MAX_ADDRS 3
struct adv7511 {
struct i2c_client *i2c_main;
struct i2c_client *i2c_edid;
struct i2c_client *i2c_packet;
struct i2c_client *i2c_cec;
struct regmap *regmap;
struct regmap *regmap_cec;
enum drm_connector_status status;
bool powered;
struct drm_display_mode curr_mode;
unsigned int f_tmds;
unsigned int f_audio;
unsigned int audio_source;
unsigned int current_edid_segment;
uint8_t edid_buf[256];
bool edid_read;
wait_queue_head_t wq;
struct work_struct hpd_work;
struct drm_encoder encoder;
struct drm_bridge bridge;
struct drm_connector connector;
bool embedded_sync;
enum adv7511_sync_polarity vsync_polarity;
enum adv7511_sync_polarity hsync_polarity;
bool rgb;
struct gpio_desc *gpio_pd;
struct regulator_bulk_data *supplies;
unsigned int num_supplies;
/* ADV7533 DSI RX related params */
struct device_node *host_node;
struct mipi_dsi_device *dsi;
u8 num_dsi_lanes;
bool use_timing_gen;
enum adv7511_type type;
struct platform_device *audio_pdev;
struct cec_adapter *cec_adap;
u8 cec_addr[ADV7511_MAX_ADDRS];
u8 cec_valid_addrs;
bool cec_enabled_adap;
struct clk *cec_clk;
u32 cec_clk_freq;
};
#ifdef CONFIG_DRM_I2C_ADV7511_CEC
int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511);
void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1);
#else
static inline int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511)
{
unsigned int offset = adv7511->type == ADV7533 ?
ADV7533_REG_CEC_OFFSET : 0;
regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset,
ADV7511_CEC_CTRL_POWER_DOWN);
return 0;
}
#endif
void adv7533_dsi_power_on(struct adv7511 *adv);
void adv7533_dsi_power_off(struct adv7511 *adv);
void adv7533_mode_set(struct adv7511 *adv, const struct drm_display_mode *mode);
int adv7533_patch_registers(struct adv7511 *adv);
int adv7533_patch_cec_registers(struct adv7511 *adv);
int adv7533_attach_dsi(struct adv7511 *adv);
void adv7533_detach_dsi(struct adv7511 *adv);
int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv);
#ifdef CONFIG_DRM_I2C_ADV7511_AUDIO
int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511);
void adv7511_audio_exit(struct adv7511 *adv7511);
#else /*CONFIG_DRM_I2C_ADV7511_AUDIO */
static inline int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511)
{
return 0;
}
static inline void adv7511_audio_exit(struct adv7511 *adv7511)
{
}
#endif /* CONFIG_DRM_I2C_ADV7511_AUDIO */
#endif /* __DRM_I2C_ADV7511_H__ */

View file

@ -0,0 +1,250 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Analog Devices ADV7511 HDMI transmitter driver
*
* Copyright 2012 Analog Devices Inc.
* Copyright (c) 2016, Linaro Limited
*/
#include <sound/core.h>
#include <sound/hdmi-codec.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <linux/of_graph.h>
#include "adv7511.h"
static void adv7511_calc_cts_n(unsigned int f_tmds, unsigned int fs,
unsigned int *cts, unsigned int *n)
{
switch (fs) {
case 32000:
case 48000:
case 96000:
case 192000:
*n = fs * 128 / 1000;
break;
case 44100:
case 88200:
case 176400:
*n = fs * 128 / 900;
break;
}
*cts = ((f_tmds * *n) / (128 * fs)) * 1000;
}
static int adv7511_update_cts_n(struct adv7511 *adv7511)
{
unsigned int cts = 0;
unsigned int n = 0;
adv7511_calc_cts_n(adv7511->f_tmds, adv7511->f_audio, &cts, &n);
regmap_write(adv7511->regmap, ADV7511_REG_N0, (n >> 16) & 0xf);
regmap_write(adv7511->regmap, ADV7511_REG_N1, (n >> 8) & 0xff);
regmap_write(adv7511->regmap, ADV7511_REG_N2, n & 0xff);
regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL0,
(cts >> 16) & 0xf);
regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL1,
(cts >> 8) & 0xff);
regmap_write(adv7511->regmap, ADV7511_REG_CTS_MANUAL2,
cts & 0xff);
return 0;
}
int adv7511_hdmi_hw_params(struct device *dev, void *data,
struct hdmi_codec_daifmt *fmt,
struct hdmi_codec_params *hparms)
{
struct adv7511 *adv7511 = dev_get_drvdata(dev);
unsigned int audio_source, i2s_format = 0;
unsigned int invert_clock;
unsigned int rate;
unsigned int len;
switch (hparms->sample_rate) {
case 32000:
rate = ADV7511_SAMPLE_FREQ_32000;
break;
case 44100:
rate = ADV7511_SAMPLE_FREQ_44100;
break;
case 48000:
rate = ADV7511_SAMPLE_FREQ_48000;
break;
case 88200:
rate = ADV7511_SAMPLE_FREQ_88200;
break;
case 96000:
rate = ADV7511_SAMPLE_FREQ_96000;
break;
case 176400:
rate = ADV7511_SAMPLE_FREQ_176400;
break;
case 192000:
rate = ADV7511_SAMPLE_FREQ_192000;
break;
default:
return -EINVAL;
}
switch (hparms->sample_width) {
case 16:
len = ADV7511_I2S_SAMPLE_LEN_16;
break;
case 18:
len = ADV7511_I2S_SAMPLE_LEN_18;
break;
case 20:
len = ADV7511_I2S_SAMPLE_LEN_20;
break;
case 24:
len = ADV7511_I2S_SAMPLE_LEN_24;
break;
default:
return -EINVAL;
}
switch (fmt->fmt) {
case HDMI_I2S:
audio_source = ADV7511_AUDIO_SOURCE_I2S;
i2s_format = ADV7511_I2S_FORMAT_I2S;
break;
case HDMI_RIGHT_J:
audio_source = ADV7511_AUDIO_SOURCE_I2S;
i2s_format = ADV7511_I2S_FORMAT_RIGHT_J;
break;
case HDMI_LEFT_J:
audio_source = ADV7511_AUDIO_SOURCE_I2S;
i2s_format = ADV7511_I2S_FORMAT_LEFT_J;
break;
case HDMI_SPDIF:
audio_source = ADV7511_AUDIO_SOURCE_SPDIF;
break;
default:
return -EINVAL;
}
invert_clock = fmt->bit_clk_inv;
regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_SOURCE, 0x70,
audio_source << 4);
regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, BIT(6),
invert_clock << 6);
regmap_update_bits(adv7511->regmap, ADV7511_REG_I2S_CONFIG, 0x03,
i2s_format);
adv7511->audio_source = audio_source;
adv7511->f_audio = hparms->sample_rate;
adv7511_update_cts_n(adv7511);
regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CFG3,
ADV7511_AUDIO_CFG3_LEN_MASK, len);
regmap_update_bits(adv7511->regmap, ADV7511_REG_I2C_FREQ_ID_CFG,
ADV7511_I2C_FREQ_ID_CFG_RATE_MASK, rate << 4);
regmap_write(adv7511->regmap, 0x73, 0x1);
return 0;
}
static int audio_startup(struct device *dev, void *data)
{
struct adv7511 *adv7511 = dev_get_drvdata(dev);
regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG,
BIT(7), 0);
/* hide Audio infoframe updates */
regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
BIT(5), BIT(5));
/* enable N/CTS, enable Audio sample packets */
regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
BIT(5), BIT(5));
/* enable N/CTS */
regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
BIT(6), BIT(6));
/* not copyrighted */
regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CFG1,
BIT(5), BIT(5));
/* enable audio infoframes */
regmap_update_bits(adv7511->regmap, ADV7511_REG_PACKET_ENABLE1,
BIT(3), BIT(3));
/* AV mute disable */
regmap_update_bits(adv7511->regmap, ADV7511_REG_GC(0),
BIT(7) | BIT(6), BIT(7));
/* use Audio infoframe updated info */
regmap_update_bits(adv7511->regmap, ADV7511_REG_GC(1),
BIT(5), 0);
/* enable SPDIF receiver */
if (adv7511->audio_source == ADV7511_AUDIO_SOURCE_SPDIF)
regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG,
BIT(7), BIT(7));
return 0;
}
static void audio_shutdown(struct device *dev, void *data)
{
struct adv7511 *adv7511 = dev_get_drvdata(dev);
if (adv7511->audio_source == ADV7511_AUDIO_SOURCE_SPDIF)
regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG,
BIT(7), 0);
}
static int adv7511_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
struct device_node *endpoint)
{
struct of_endpoint of_ep;
int ret;
ret = of_graph_parse_endpoint(endpoint, &of_ep);
if (ret < 0)
return ret;
/*
* HDMI sound should be located as reg = <2>
* Then, it is sound port 0
*/
if (of_ep.port == 2)
return 0;
return -EINVAL;
}
static const struct hdmi_codec_ops adv7511_codec_ops = {
.hw_params = adv7511_hdmi_hw_params,
.audio_shutdown = audio_shutdown,
.audio_startup = audio_startup,
.get_dai_id = adv7511_hdmi_i2s_get_dai_id,
};
static const struct hdmi_codec_pdata codec_data = {
.ops = &adv7511_codec_ops,
.max_i2s_channels = 2,
.i2s = 1,
.spdif = 1,
};
int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511)
{
adv7511->audio_pdev = platform_device_register_data(dev,
HDMI_CODEC_DRV_NAME,
PLATFORM_DEVID_AUTO,
&codec_data,
sizeof(codec_data));
return PTR_ERR_OR_ZERO(adv7511->audio_pdev);
}
void adv7511_audio_exit(struct adv7511 *adv7511)
{
if (adv7511->audio_pdev) {
platform_device_unregister(adv7511->audio_pdev);
adv7511->audio_pdev = NULL;
}
}

View file

@ -0,0 +1,349 @@
/*
* adv7511_cec.c - Analog Devices ADV7511/33 cec driver
*
* Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*
* This program is free software; you may redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <media/cec.h>
#include "adv7511.h"
#define ADV7511_INT1_CEC_MASK \
(ADV7511_INT1_CEC_TX_READY | ADV7511_INT1_CEC_TX_ARBIT_LOST | \
ADV7511_INT1_CEC_TX_RETRY_TIMEOUT | ADV7511_INT1_CEC_RX_READY1)
static void adv_cec_tx_raw_status(struct adv7511 *adv7511, u8 tx_raw_status)
{
unsigned int offset = adv7511->type == ADV7533 ?
ADV7533_REG_CEC_OFFSET : 0;
unsigned int val;
if (regmap_read(adv7511->regmap_cec,
ADV7511_REG_CEC_TX_ENABLE + offset, &val))
return;
if ((val & 0x01) == 0)
return;
if (tx_raw_status & ADV7511_INT1_CEC_TX_ARBIT_LOST) {
cec_transmit_attempt_done(adv7511->cec_adap,
CEC_TX_STATUS_ARB_LOST);
return;
}
if (tx_raw_status & ADV7511_INT1_CEC_TX_RETRY_TIMEOUT) {
u8 status;
u8 err_cnt = 0;
u8 nack_cnt = 0;
u8 low_drive_cnt = 0;
unsigned int cnt;
/*
* We set this status bit since this hardware performs
* retransmissions.
*/
status = CEC_TX_STATUS_MAX_RETRIES;
if (regmap_read(adv7511->regmap_cec,
ADV7511_REG_CEC_TX_LOW_DRV_CNT + offset, &cnt)) {
err_cnt = 1;
status |= CEC_TX_STATUS_ERROR;
} else {
nack_cnt = cnt & 0xf;
if (nack_cnt)
status |= CEC_TX_STATUS_NACK;
low_drive_cnt = cnt >> 4;
if (low_drive_cnt)
status |= CEC_TX_STATUS_LOW_DRIVE;
}
cec_transmit_done(adv7511->cec_adap, status,
0, nack_cnt, low_drive_cnt, err_cnt);
return;
}
if (tx_raw_status & ADV7511_INT1_CEC_TX_READY) {
cec_transmit_attempt_done(adv7511->cec_adap, CEC_TX_STATUS_OK);
return;
}
}
void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1)
{
unsigned int offset = adv7511->type == ADV7533 ?
ADV7533_REG_CEC_OFFSET : 0;
const u32 irq_tx_mask = ADV7511_INT1_CEC_TX_READY |
ADV7511_INT1_CEC_TX_ARBIT_LOST |
ADV7511_INT1_CEC_TX_RETRY_TIMEOUT;
struct cec_msg msg = {};
unsigned int len;
unsigned int val;
u8 i;
if (irq1 & irq_tx_mask)
adv_cec_tx_raw_status(adv7511, irq1);
if (!(irq1 & ADV7511_INT1_CEC_RX_READY1))
return;
if (regmap_read(adv7511->regmap_cec,
ADV7511_REG_CEC_RX_FRAME_LEN + offset, &len))
return;
msg.len = len & 0x1f;
if (msg.len > 16)
msg.len = 16;
if (!msg.len)
return;
for (i = 0; i < msg.len; i++) {
regmap_read(adv7511->regmap_cec,
i + ADV7511_REG_CEC_RX_FRAME_HDR + offset, &val);
msg.msg[i] = val;
}
/* toggle to re-enable rx 1 */
regmap_write(adv7511->regmap_cec,
ADV7511_REG_CEC_RX_BUFFERS + offset, 1);
regmap_write(adv7511->regmap_cec,
ADV7511_REG_CEC_RX_BUFFERS + offset, 0);
cec_received_msg(adv7511->cec_adap, &msg);
}
static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable)
{
struct adv7511 *adv7511 = cec_get_drvdata(adap);
unsigned int offset = adv7511->type == ADV7533 ?
ADV7533_REG_CEC_OFFSET : 0;
if (adv7511->i2c_cec == NULL)
return -EIO;
if (!adv7511->cec_enabled_adap && enable) {
/* power up cec section */
regmap_update_bits(adv7511->regmap_cec,
ADV7511_REG_CEC_CLK_DIV + offset,
0x03, 0x01);
/* legacy mode and clear all rx buffers */
regmap_write(adv7511->regmap_cec,
ADV7511_REG_CEC_RX_BUFFERS + offset, 0x07);
regmap_write(adv7511->regmap_cec,
ADV7511_REG_CEC_RX_BUFFERS + offset, 0);
/* initially disable tx */
regmap_update_bits(adv7511->regmap_cec,
ADV7511_REG_CEC_TX_ENABLE + offset, 1, 0);
/* enabled irqs: */
/* tx: ready */
/* tx: arbitration lost */
/* tx: retry timeout */
/* rx: ready 1 */
regmap_update_bits(adv7511->regmap,
ADV7511_REG_INT_ENABLE(1), 0x3f,
ADV7511_INT1_CEC_MASK);
} else if (adv7511->cec_enabled_adap && !enable) {
regmap_update_bits(adv7511->regmap,
ADV7511_REG_INT_ENABLE(1), 0x3f, 0);
/* disable address mask 1-3 */
regmap_update_bits(adv7511->regmap_cec,
ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
0x70, 0x00);
/* power down cec section */
regmap_update_bits(adv7511->regmap_cec,
ADV7511_REG_CEC_CLK_DIV + offset,
0x03, 0x00);
adv7511->cec_valid_addrs = 0;
}
adv7511->cec_enabled_adap = enable;
return 0;
}
static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
{
struct adv7511 *adv7511 = cec_get_drvdata(adap);
unsigned int offset = adv7511->type == ADV7533 ?
ADV7533_REG_CEC_OFFSET : 0;
unsigned int i, free_idx = ADV7511_MAX_ADDRS;
if (!adv7511->cec_enabled_adap)
return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;
if (addr == CEC_LOG_ADDR_INVALID) {
regmap_update_bits(adv7511->regmap_cec,
ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
0x70, 0);
adv7511->cec_valid_addrs = 0;
return 0;
}
for (i = 0; i < ADV7511_MAX_ADDRS; i++) {
bool is_valid = adv7511->cec_valid_addrs & (1 << i);
if (free_idx == ADV7511_MAX_ADDRS && !is_valid)
free_idx = i;
if (is_valid && adv7511->cec_addr[i] == addr)
return 0;
}
if (i == ADV7511_MAX_ADDRS) {
i = free_idx;
if (i == ADV7511_MAX_ADDRS)
return -ENXIO;
}
adv7511->cec_addr[i] = addr;
adv7511->cec_valid_addrs |= 1 << i;
switch (i) {
case 0:
/* enable address mask 0 */
regmap_update_bits(adv7511->regmap_cec,
ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
0x10, 0x10);
/* set address for mask 0 */
regmap_update_bits(adv7511->regmap_cec,
ADV7511_REG_CEC_LOG_ADDR_0_1 + offset,
0x0f, addr);
break;
case 1:
/* enable address mask 1 */
regmap_update_bits(adv7511->regmap_cec,
ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
0x20, 0x20);
/* set address for mask 1 */
regmap_update_bits(adv7511->regmap_cec,
ADV7511_REG_CEC_LOG_ADDR_0_1 + offset,
0xf0, addr << 4);
break;
case 2:
/* enable address mask 2 */
regmap_update_bits(adv7511->regmap_cec,
ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
0x40, 0x40);
/* set address for mask 1 */
regmap_update_bits(adv7511->regmap_cec,
ADV7511_REG_CEC_LOG_ADDR_2 + offset,
0x0f, addr);
break;
}
return 0;
}
static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
u32 signal_free_time, struct cec_msg *msg)
{
struct adv7511 *adv7511 = cec_get_drvdata(adap);
unsigned int offset = adv7511->type == ADV7533 ?
ADV7533_REG_CEC_OFFSET : 0;
u8 len = msg->len;
unsigned int i;
/*
* The number of retries is the number of attempts - 1, but retry
* at least once. It's not clear if a value of 0 is allowed, so
* let's do at least one retry.
*/
regmap_update_bits(adv7511->regmap_cec,
ADV7511_REG_CEC_TX_RETRY + offset,
0x70, max(1, attempts - 1) << 4);
/* blocking, clear cec tx irq status */
regmap_update_bits(adv7511->regmap, ADV7511_REG_INT(1), 0x38, 0x38);
/* write data */
for (i = 0; i < len; i++)
regmap_write(adv7511->regmap_cec,
i + ADV7511_REG_CEC_TX_FRAME_HDR + offset,
msg->msg[i]);
/* set length (data + header) */
regmap_write(adv7511->regmap_cec,
ADV7511_REG_CEC_TX_FRAME_LEN + offset, len);
/* start transmit, enable tx */
regmap_write(adv7511->regmap_cec,
ADV7511_REG_CEC_TX_ENABLE + offset, 0x01);
return 0;
}
static const struct cec_adap_ops adv7511_cec_adap_ops = {
.adap_enable = adv7511_cec_adap_enable,
.adap_log_addr = adv7511_cec_adap_log_addr,
.adap_transmit = adv7511_cec_adap_transmit,
};
static int adv7511_cec_parse_dt(struct device *dev, struct adv7511 *adv7511)
{
adv7511->cec_clk = devm_clk_get(dev, "cec");
if (IS_ERR(adv7511->cec_clk)) {
int ret = PTR_ERR(adv7511->cec_clk);
adv7511->cec_clk = NULL;
return ret;
}
clk_prepare_enable(adv7511->cec_clk);
adv7511->cec_clk_freq = clk_get_rate(adv7511->cec_clk);
return 0;
}
int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511)
{
unsigned int offset = adv7511->type == ADV7533 ?
ADV7533_REG_CEC_OFFSET : 0;
int ret = adv7511_cec_parse_dt(dev, adv7511);
if (ret)
goto err_cec_parse_dt;
adv7511->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops,
adv7511, dev_name(dev), CEC_CAP_DEFAULTS, ADV7511_MAX_ADDRS);
if (IS_ERR(adv7511->cec_adap)) {
ret = PTR_ERR(adv7511->cec_adap);
goto err_cec_alloc;
}
regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset, 0);
/* cec soft reset */
regmap_write(adv7511->regmap_cec,
ADV7511_REG_CEC_SOFT_RESET + offset, 0x01);
regmap_write(adv7511->regmap_cec,
ADV7511_REG_CEC_SOFT_RESET + offset, 0x00);
/* legacy mode */
regmap_write(adv7511->regmap_cec,
ADV7511_REG_CEC_RX_BUFFERS + offset, 0x00);
regmap_write(adv7511->regmap_cec,
ADV7511_REG_CEC_CLK_DIV + offset,
((adv7511->cec_clk_freq / 750000) - 1) << 2);
ret = cec_register_adapter(adv7511->cec_adap, dev);
if (ret)
goto err_cec_register;
return 0;
err_cec_register:
cec_delete_adapter(adv7511->cec_adap);
adv7511->cec_adap = NULL;
err_cec_alloc:
dev_info(dev, "Initializing CEC failed with error %d, disabling CEC\n",
ret);
err_cec_parse_dt:
regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset,
ADV7511_CEC_CTRL_POWER_DOWN);
return ret == -EPROBE_DEFER ? ret : 0;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,215 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016, The Linux Foundation. All rights reserved.
*/
#include <linux/of_graph.h>
#include "adv7511.h"
static const struct reg_sequence adv7533_fixed_registers[] = {
{ 0x16, 0x20 },
{ 0x9a, 0xe0 },
{ 0xba, 0x70 },
{ 0xde, 0x82 },
{ 0xe4, 0x40 },
{ 0xe5, 0x80 },
};
static const struct reg_sequence adv7533_cec_fixed_registers[] = {
{ 0x15, 0xd0 },
{ 0x17, 0xd0 },
{ 0x24, 0x20 },
{ 0x57, 0x11 },
{ 0x05, 0xc8 },
};
static void adv7511_dsi_config_timing_gen(struct adv7511 *adv)
{
struct mipi_dsi_device *dsi = adv->dsi;
struct drm_display_mode *mode = &adv->curr_mode;
unsigned int hsw, hfp, hbp, vsw, vfp, vbp;
u8 clock_div_by_lanes[] = { 6, 4, 3 }; /* 2, 3, 4 lanes */
hsw = mode->hsync_end - mode->hsync_start;
hfp = mode->hsync_start - mode->hdisplay;
hbp = mode->htotal - mode->hsync_end;
vsw = mode->vsync_end - mode->vsync_start;
vfp = mode->vsync_start - mode->vdisplay;
vbp = mode->vtotal - mode->vsync_end;
/* set pixel clock divider mode */
regmap_write(adv->regmap_cec, 0x16,
clock_div_by_lanes[dsi->lanes - 2] << 3);
/* horizontal porch params */
regmap_write(adv->regmap_cec, 0x28, mode->htotal >> 4);
regmap_write(adv->regmap_cec, 0x29, (mode->htotal << 4) & 0xff);
regmap_write(adv->regmap_cec, 0x2a, hsw >> 4);
regmap_write(adv->regmap_cec, 0x2b, (hsw << 4) & 0xff);
regmap_write(adv->regmap_cec, 0x2c, hfp >> 4);
regmap_write(adv->regmap_cec, 0x2d, (hfp << 4) & 0xff);
regmap_write(adv->regmap_cec, 0x2e, hbp >> 4);
regmap_write(adv->regmap_cec, 0x2f, (hbp << 4) & 0xff);
/* vertical porch params */
regmap_write(adv->regmap_cec, 0x30, mode->vtotal >> 4);
regmap_write(adv->regmap_cec, 0x31, (mode->vtotal << 4) & 0xff);
regmap_write(adv->regmap_cec, 0x32, vsw >> 4);
regmap_write(adv->regmap_cec, 0x33, (vsw << 4) & 0xff);
regmap_write(adv->regmap_cec, 0x34, vfp >> 4);
regmap_write(adv->regmap_cec, 0x35, (vfp << 4) & 0xff);
regmap_write(adv->regmap_cec, 0x36, vbp >> 4);
regmap_write(adv->regmap_cec, 0x37, (vbp << 4) & 0xff);
}
void adv7533_dsi_power_on(struct adv7511 *adv)
{
struct mipi_dsi_device *dsi = adv->dsi;
if (adv->use_timing_gen)
adv7511_dsi_config_timing_gen(adv);
/* set number of dsi lanes */
regmap_write(adv->regmap_cec, 0x1c, dsi->lanes << 4);
if (adv->use_timing_gen) {
/* reset internal timing generator */
regmap_write(adv->regmap_cec, 0x27, 0xcb);
regmap_write(adv->regmap_cec, 0x27, 0x8b);
regmap_write(adv->regmap_cec, 0x27, 0xcb);
} else {
/* disable internal timing generator */
regmap_write(adv->regmap_cec, 0x27, 0x0b);
}
/* enable hdmi */
regmap_write(adv->regmap_cec, 0x03, 0x89);
/* disable test mode */
regmap_write(adv->regmap_cec, 0x55, 0x00);
regmap_register_patch(adv->regmap_cec, adv7533_cec_fixed_registers,
ARRAY_SIZE(adv7533_cec_fixed_registers));
}
void adv7533_dsi_power_off(struct adv7511 *adv)
{
/* disable hdmi */
regmap_write(adv->regmap_cec, 0x03, 0x0b);
/* disable internal timing generator */
regmap_write(adv->regmap_cec, 0x27, 0x0b);
}
void adv7533_mode_set(struct adv7511 *adv, const struct drm_display_mode *mode)
{
struct mipi_dsi_device *dsi = adv->dsi;
int lanes, ret;
if (adv->num_dsi_lanes != 4)
return;
if (mode->clock > 80000)
lanes = 4;
else
lanes = 3;
if (lanes != dsi->lanes) {
mipi_dsi_detach(dsi);
dsi->lanes = lanes;
ret = mipi_dsi_attach(dsi);
if (ret)
dev_err(&dsi->dev, "failed to change host lanes\n");
}
}
int adv7533_patch_registers(struct adv7511 *adv)
{
return regmap_register_patch(adv->regmap,
adv7533_fixed_registers,
ARRAY_SIZE(adv7533_fixed_registers));
}
int adv7533_patch_cec_registers(struct adv7511 *adv)
{
return regmap_register_patch(adv->regmap_cec,
adv7533_cec_fixed_registers,
ARRAY_SIZE(adv7533_cec_fixed_registers));
}
int adv7533_attach_dsi(struct adv7511 *adv)
{
struct device *dev = &adv->i2c_main->dev;
struct mipi_dsi_host *host;
struct mipi_dsi_device *dsi;
int ret = 0;
const struct mipi_dsi_device_info info = { .type = "adv7533",
.channel = 0,
.node = NULL,
};
host = of_find_mipi_dsi_host_by_node(adv->host_node);
if (!host) {
dev_err(dev, "failed to find dsi host\n");
return -EPROBE_DEFER;
}
dsi = mipi_dsi_device_register_full(host, &info);
if (IS_ERR(dsi)) {
dev_err(dev, "failed to create dsi device\n");
ret = PTR_ERR(dsi);
goto err_dsi_device;
}
adv->dsi = dsi;
dsi->lanes = adv->num_dsi_lanes;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_MODE_VIDEO_HSE;
ret = mipi_dsi_attach(dsi);
if (ret < 0) {
dev_err(dev, "failed to attach dsi to host\n");
goto err_dsi_attach;
}
return 0;
err_dsi_attach:
mipi_dsi_device_unregister(dsi);
err_dsi_device:
return ret;
}
void adv7533_detach_dsi(struct adv7511 *adv)
{
mipi_dsi_detach(adv->dsi);
mipi_dsi_device_unregister(adv->dsi);
}
int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv)
{
u32 num_lanes;
of_property_read_u32(np, "adi,dsi-lanes", &num_lanes);
if (num_lanes < 1 || num_lanes > 4)
return -EINVAL;
adv->num_dsi_lanes = num_lanes;
adv->host_node = of_graph_get_remote_node(np, 0, 0);
if (!adv->host_node)
return -ENODEV;
of_node_put(adv->host_node);
adv->use_timing_gen = !of_property_read_bool(np,
"adi,disable-timing-generator");
/* TODO: Check if these need to be parsed by DT or not */
adv->rgb = true;
adv->embedded_sync = false;
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,599 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
* Zheng Yang <zhengyang@rock-chips.com>
* Yakir Yang <ykk@rock-chips.com>
*/
#ifndef __INNO_HDMI_H__
#define __INNO_HDMI_H__
#include <drm/display/drm_scdc_helper.h>
#include <drm/bridge/dw_hdmi.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#define DDC_SEGMENT_ADDR 0x30
enum PWR_MODE {
NORMAL,
LOWER_PWR,
};
#define HDMI_SCL_RATE (100*1000)
#define DDC_BUS_FREQ_L 0x4b
#define DDC_BUS_FREQ_H 0x4c
#define HDMI_SYS_CTRL 0x00
#define m_RST_ANALOG (1 << 6)
#define v_RST_ANALOG (0 << 6)
#define v_NOT_RST_ANALOG (1 << 6)
#define m_RST_DIGITAL (1 << 5)
#define v_RST_DIGITAL (0 << 5)
#define v_NOT_RST_DIGITAL (1 << 5)
#define m_REG_CLK_INV (1 << 4)
#define v_REG_CLK_NOT_INV (0 << 4)
#define v_REG_CLK_INV (1 << 4)
#define m_VCLK_INV (1 << 3)
#define v_VCLK_NOT_INV (0 << 3)
#define v_VCLK_INV (1 << 3)
#define m_REG_CLK_SOURCE (1 << 2)
#define v_REG_CLK_SOURCE_TMDS (0 << 2)
#define v_REG_CLK_SOURCE_SYS (1 << 2)
#define m_POWER (1 << 1)
#define v_PWR_ON (0 << 1)
#define v_PWR_OFF (1 << 1)
#define m_INT_POL (1 << 0)
#define v_INT_POL_HIGH 1
#define v_INT_POL_LOW 0
#define HDMI_VIDEO_CONTRL1 0x01
#define m_VIDEO_INPUT_FORMAT (7 << 1)
#define m_DE_SOURCE (1 << 0)
#define v_VIDEO_INPUT_FORMAT(n) (n << 1)
#define v_DE_EXTERNAL 1
#define v_DE_INTERNAL 0
enum {
VIDEO_INPUT_SDR_RGB444 = 0,
VIDEO_INPUT_DDR_RGB444 = 5,
VIDEO_INPUT_DDR_YCBCR422 = 6
};
#define HDMI_VIDEO_CONTRL2 0x02
#define m_VIDEO_OUTPUT_COLOR (3 << 6)
#define m_VIDEO_INPUT_BITS (3 << 4)
#define m_VIDEO_INPUT_CSP (1 << 0)
#define v_VIDEO_OUTPUT_COLOR(n) (((n) & 0x3) << 6)
#define v_VIDEO_INPUT_BITS(n) (n << 4)
#define v_VIDEO_INPUT_CSP(n) (n << 0)
enum {
VIDEO_INPUT_12BITS = 0,
VIDEO_INPUT_10BITS = 1,
VIDEO_INPUT_REVERT = 2,
VIDEO_INPUT_8BITS = 3,
};
#define HDMI_VIDEO_CONTRL 0x03
#define m_VIDEO_AUTO_CSC (1 << 7)
#define v_VIDEO_AUTO_CSC(n) (n << 7)
#define m_VIDEO_C0_C2_SWAP (1 << 0)
#define v_VIDEO_C0_C2_SWAP(n) (n << 0)
enum {
C0_C2_CHANGE_ENABLE = 0,
C0_C2_CHANGE_DISABLE = 1,
AUTO_CSC_DISABLE = 0,
AUTO_CSC_ENABLE = 1,
};
#define HDMI_VIDEO_CONTRL3 0x04
#define m_COLOR_DEPTH_NOT_INDICATED (1 << 4)
#define m_SOF (1 << 3)
#define m_COLOR_RANGE (1 << 2)
#define m_CSC (1 << 0)
#define v_COLOR_DEPTH_NOT_INDICATED(n) ((n) << 4)
#define v_SOF_ENABLE (0 << 3)
#define v_SOF_DISABLE (1 << 3)
#define v_COLOR_RANGE_FULL (1 << 2)
#define v_COLOR_RANGE_LIMITED (0 << 2)
#define v_CSC_ENABLE 1
#define v_CSC_DISABLE 0
#define HDMI_AV_MUTE 0x05
#define m_AVMUTE_CLEAR (1 << 7)
#define m_AVMUTE_ENABLE (1 << 6)
#define m_AUDIO_MUTE (1 << 1)
#define m_VIDEO_BLACK (1 << 0)
#define v_AVMUTE_CLEAR(n) (n << 7)
#define v_AVMUTE_ENABLE(n) (n << 6)
#define v_AUDIO_MUTE(n) (n << 1)
#define v_VIDEO_MUTE(n) (n << 0)
#define HDMI_VIDEO_TIMING_CTL 0x08
#define v_VSYNC_POLARITY(n) (n << 3)
#define v_HSYNC_POLARITY(n) (n << 2)
#define v_INETLACE(n) (n << 1)
#define v_EXTERANL_VIDEO(n) (n << 0)
#define HDMI_VIDEO_EXT_HTOTAL_L 0x09
#define HDMI_VIDEO_EXT_HTOTAL_H 0x0a
#define HDMI_VIDEO_EXT_HBLANK_L 0x0b
#define HDMI_VIDEO_EXT_HBLANK_H 0x0c
#define HDMI_VIDEO_EXT_HDELAY_L 0x0d
#define HDMI_VIDEO_EXT_HDELAY_H 0x0e
#define HDMI_VIDEO_EXT_HDURATION_L 0x0f
#define HDMI_VIDEO_EXT_HDURATION_H 0x10
#define HDMI_VIDEO_EXT_VTOTAL_L 0x11
#define HDMI_VIDEO_EXT_VTOTAL_H 0x12
#define HDMI_VIDEO_EXT_VBLANK 0x13
#define HDMI_VIDEO_EXT_VDELAY 0x14
#define HDMI_VIDEO_EXT_VDURATION 0x15
#define HDMI_VIDEO_CSC_COEF 0x18
#define HDMI_AUDIO_CTRL1 0x35
enum {
CTS_SOURCE_INTERNAL = 0,
CTS_SOURCE_EXTERNAL = 1,
};
#define v_CTS_SOURCE(n) (n << 7)
enum {
DOWNSAMPLE_DISABLE = 0,
DOWNSAMPLE_1_2 = 1,
DOWNSAMPLE_1_4 = 2,
};
#define v_DOWN_SAMPLE(n) (n << 5)
enum {
AUDIO_SOURCE_IIS = 0,
AUDIO_SOURCE_SPDIF = 1,
};
#define v_AUDIO_SOURCE(n) (n << 3)
#define v_MCLK_ENABLE(n) (n << 2)
enum {
MCLK_128FS = 0,
MCLK_256FS = 1,
MCLK_384FS = 2,
MCLK_512FS = 3,
};
#define v_MCLK_RATIO(n) (n)
#define AUDIO_SAMPLE_RATE 0x37
enum {
AUDIO_32K = 0x3,
AUDIO_441K = 0x0,
AUDIO_48K = 0x2,
AUDIO_882K = 0x8,
AUDIO_96K = 0xa,
AUDIO_1764K = 0xc,
AUDIO_192K = 0xe,
};
#define AUDIO_I2S_MODE 0x38
enum {
I2S_CHANNEL_1_2 = 1,
I2S_CHANNEL_3_4 = 3,
I2S_CHANNEL_5_6 = 7,
I2S_CHANNEL_7_8 = 0xf
};
#define v_I2S_CHANNEL(n) ((n) << 2)
enum {
I2S_STANDARD = 0,
I2S_LEFT_JUSTIFIED = 1,
I2S_RIGHT_JUSTIFIED = 2,
};
#define v_I2S_MODE(n) (n)
#define AUDIO_I2S_MAP 0x39
#define AUDIO_I2S_SWAPS_SPDIF 0x3a
#define v_SPIDF_FREQ(n) (n)
#define N_32K 0x1000
#define N_441K 0x1880
#define N_882K 0x3100
#define N_1764K 0x6200
#define N_48K 0x1800
#define N_96K 0x3000
#define N_192K 0x6000
#define HDMI_AUDIO_CHANNEL_STATUS 0x3e
#define m_AUDIO_STATUS_NLPCM (1 << 7)
#define m_AUDIO_STATUS_USE (1 << 6)
#define m_AUDIO_STATUS_COPYRIGHT (1 << 5)
#define m_AUDIO_STATUS_ADDITION (3 << 2)
#define m_AUDIO_STATUS_CLK_ACCURACY (2 << 0)
#define v_AUDIO_STATUS_NLPCM(n) ((n & 1) << 7)
#define AUDIO_N_H 0x3f
#define AUDIO_N_M 0x40
#define AUDIO_N_L 0x41
#define HDMI_AUDIO_CTS_H 0x45
#define HDMI_AUDIO_CTS_M 0x46
#define HDMI_AUDIO_CTS_L 0x47
#define HDMI_DDC_CLK_L 0x4b
#define HDMI_DDC_CLK_H 0x4c
#define HDMI_EDID_SEGMENT_POINTER 0x4d
#define HDMI_EDID_WORD_ADDR 0x4e
#define HDMI_EDID_FIFO_OFFSET 0x4f
#define HDMI_EDID_FIFO_ADDR 0x50
#define HDMI_PACKET_SEND_MANUAL 0x9c
#define HDMI_PACKET_SEND_AUTO 0x9d
#define m_PACKET_GCP_EN (1 << 7)
#define m_PACKET_MSI_EN (1 << 6)
#define m_PACKET_SDI_EN (1 << 5)
#define m_PACKET_VSI_EN (1 << 4)
#define v_PACKET_GCP_EN(n) ((n & 1) << 7)
#define v_PACKET_MSI_EN(n) ((n & 1) << 6)
#define v_PACKET_SDI_EN(n) ((n & 1) << 5)
#define v_PACKET_VSI_EN(n) ((n & 1) << 4)
#define HDMI_CONTROL_PACKET_BUF_INDEX 0x9f
enum {
INFOFRAME_VSI = 0x05,
INFOFRAME_AVI = 0x06,
INFOFRAME_AAI = 0x08,
};
#define HDMI_CONTROL_PACKET_ADDR 0xa0
#define HDMI_MAXIMUM_INFO_FRAME_SIZE 0x11
enum {
AVI_COLOR_MODE_RGB = 0,
AVI_COLOR_MODE_YCBCR422 = 1,
AVI_COLOR_MODE_YCBCR444 = 2,
AVI_COLORIMETRY_NO_DATA = 0,
AVI_COLORIMETRY_SMPTE_170M = 1,
AVI_COLORIMETRY_ITU709 = 2,
AVI_COLORIMETRY_EXTENDED = 3,
AVI_CODED_FRAME_ASPECT_NO_DATA = 0,
AVI_CODED_FRAME_ASPECT_4_3 = 1,
AVI_CODED_FRAME_ASPECT_16_9 = 2,
ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08,
ACTIVE_ASPECT_RATE_4_3 = 0x09,
ACTIVE_ASPECT_RATE_16_9 = 0x0A,
ACTIVE_ASPECT_RATE_14_9 = 0x0B,
};
#define HDMI_HDCP_CTRL 0x52
#define m_HDMI_DVI (1 << 1)
#define v_HDMI_DVI(n) (n << 1)
#define HDMI_INTERRUPT_MASK1 0xc0
#define HDMI_INTERRUPT_STATUS1 0xc1
#define m_INT_ACTIVE_VSYNC (1 << 5)
#define m_INT_EDID_READY (1 << 2)
#define HDMI_INTERRUPT_MASK2 0xc2
#define HDMI_INTERRUPT_STATUS2 0xc3
#define m_INT_HDCP_ERR (1 << 7)
#define m_INT_BKSV_FLAG (1 << 6)
#define m_INT_HDCP_OK (1 << 4)
#define HDMI_STATUS 0xc8
#define m_HOTPLUG (1 << 7)
#define m_MASK_INT_HOTPLUG (1 << 5)
#define m_INT_HOTPLUG (1 << 1)
#define v_MASK_INT_HOTPLUG(n) ((n & 0x1) << 5)
#define HDMI_COLORBAR 0xc9
#define HDMI_PHY_SYNC 0xce
#define HDMI_PHY_SYS_CTL 0xe0
#define m_TMDS_CLK_SOURCE (1 << 5)
#define v_TMDS_FROM_PLL (0 << 5)
#define v_TMDS_FROM_GEN (1 << 5)
#define m_PHASE_CLK (1 << 4)
#define v_DEFAULT_PHASE (0 << 4)
#define v_SYNC_PHASE (1 << 4)
#define m_TMDS_CURRENT_PWR (1 << 3)
#define v_TURN_ON_CURRENT (0 << 3)
#define v_CAT_OFF_CURRENT (1 << 3)
#define m_BANDGAP_PWR (1 << 2)
#define v_BANDGAP_PWR_UP (0 << 2)
#define v_BANDGAP_PWR_DOWN (1 << 2)
#define m_PLL_PWR (1 << 1)
#define v_PLL_PWR_UP (0 << 1)
#define v_PLL_PWR_DOWN (1 << 1)
#define m_TMDS_CHG_PWR (1 << 0)
#define v_TMDS_CHG_PWR_UP (0 << 0)
#define v_TMDS_CHG_PWR_DOWN (1 << 0)
#define HDMI_PHY_CHG_PWR 0xe1
#define v_CLK_CHG_PWR(n) ((n & 1) << 3)
#define v_DATA_CHG_PWR(n) ((n & 7) << 0)
#define HDMI_PHY_DRIVER 0xe2
#define v_CLK_MAIN_DRIVER(n) (n << 4)
#define v_DATA_MAIN_DRIVER(n) (n << 0)
#define HDMI_PHY_PRE_EMPHASIS 0xe3
#define v_PRE_EMPHASIS(n) ((n & 7) << 4)
#define v_CLK_PRE_DRIVER(n) ((n & 3) << 2)
#define v_DATA_PRE_DRIVER(n) ((n & 3) << 0)
#define HDMI_PHY_FEEDBACK_DIV_RATIO_LOW 0xe7
#define v_FEEDBACK_DIV_LOW(n) (n & 0xff)
#define HDMI_PHY_FEEDBACK_DIV_RATIO_HIGH 0xe8
#define v_FEEDBACK_DIV_HIGH(n) (n & 1)
#define HDMI_PHY_PRE_DIV_RATIO 0xed
#define v_PRE_DIV_RATIO(n) (n & 0x1f)
#define HDMI_CEC_CTRL 0xd0
#define m_ADJUST_FOR_HISENSE (1 << 6)
#define m_REJECT_RX_BROADCAST (1 << 5)
#define m_BUSFREETIME_ENABLE (1 << 2)
#define m_REJECT_RX (1 << 1)
#define m_START_TX (1 << 0)
#define HDMI_CEC_DATA 0xd1
#define HDMI_CEC_TX_OFFSET 0xd2
#define HDMI_CEC_RX_OFFSET 0xd3
#define HDMI_CEC_CLK_H 0xd4
#define HDMI_CEC_CLK_L 0xd5
#define HDMI_CEC_TX_LENGTH 0xd6
#define HDMI_CEC_RX_LENGTH 0xd7
#define HDMI_CEC_TX_INT_MASK 0xd8
#define m_TX_DONE (1 << 3)
#define m_TX_NOACK (1 << 2)
#define m_TX_BROADCAST_REJ (1 << 1)
#define m_TX_BUSNOTFREE (1 << 0)
#define HDMI_CEC_RX_INT_MASK 0xd9
#define m_RX_LA_ERR (1 << 4)
#define m_RX_GLITCH (1 << 3)
#define m_RX_DONE (1 << 0)
#define HDMI_CEC_TX_INT 0xda
#define HDMI_CEC_RX_INT 0xdb
#define HDMI_CEC_BUSFREETIME_L 0xdc
#define HDMI_CEC_BUSFREETIME_H 0xdd
#define HDMI_CEC_LOGICADDR 0xde
#define HDMI_ESD_STATUS 0x1ce
#define HDMI_REG_1A0 0x1a0
#define m_PLL_CTRL (1 << 4)
#define m_VCO_CTRL (1 << 3)
#define m_OUTPUT_CLK (1 << 2)
#define m_PIX_DIV (1 << 1)
#define m_PRE_PLL_POWER (1 << 0)
typedef enum {
VIC_1440x480i60 = 6,
VIC_640x480p60 = 1,
VIC_720x480p60 = 2,
VIC_1280x720p60 = 4,
VIC_1920x1080p60 = 16,
VIC_4096x2160p30 = 95,
VIC_4096x2160p60 = 97,
} vic_code_t;
#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
/* REG: 0x1a0 */
#define INNO_PCLK_VCO_DIV_5_MASK BIT(1)
#define INNO_PCLK_VCO_DIV_5(x) UPDATE(x, 1, 1)
#define INNO_PRE_PLL_POWER_DOWN BIT(0)
/* REG: 0x1a1 */
#define INNO_PRE_PLL_PRE_DIV_MASK GENMASK(5, 0)
#define INNO_PRE_PLL_PRE_DIV(x) UPDATE(x, 5, 0)
/* REG: 0xa2 */
/* unset means center spread */
#define INNO_SPREAD_SPECTRUM_MOD_DOWN BIT(7)
#define INNO_SPREAD_SPECTRUM_MOD_DISABLE BIT(6)
#define INNO_PRE_PLL_FRAC_DIV_DISABLE UPDATE(3, 5, 4)
#define INNO_PRE_PLL_FB_DIV_11_8_MASK GENMASK(3, 0)
#define INNO_PRE_PLL_FB_DIV_11_8(x) UPDATE((x) >> 8, 3, 0)
/* REG: 0xa3 */
#define INNO_PRE_PLL_FB_DIV_7_0(x) UPDATE(x, 7, 0)
/* REG: 0xa4*/
#define INNO_PRE_PLL_TMDSCLK_DIV_C_MASK GENMASK(1, 0)
#define INNO_PRE_PLL_TMDSCLK_DIV_C(x) UPDATE(x, 1, 0)
#define INNO_PRE_PLL_TMDSCLK_DIV_B_MASK GENMASK(3, 2)
#define INNO_PRE_PLL_TMDSCLK_DIV_B(x) UPDATE(x, 3, 2)
#define INNO_PRE_PLL_TMDSCLK_DIV_A_MASK GENMASK(5, 4)
#define INNO_PRE_PLL_TMDSCLK_DIV_A(x) UPDATE(x, 5, 4)
/* REG: 0xa5 */
#define INNO_PRE_PLL_PCLK_DIV_B_SHIFT 5
#define INNO_PRE_PLL_PCLK_DIV_B_MASK GENMASK(6, 5)
#define INNO_PRE_PLL_PCLK_DIV_B(x) UPDATE(x, 6, 5)
#define INNO_PRE_PLL_PCLK_DIV_A_MASK GENMASK(4, 0)
#define INNO_PRE_PLL_PCLK_DIV_A(x) UPDATE(x, 4, 0)
/* REG: 0xa6 */
#define INNO_PRE_PLL_PCLK_DIV_C_SHIFT 5
#define INNO_PRE_PLL_PCLK_DIV_C_MASK GENMASK(6, 5)
#define INNO_PRE_PLL_PCLK_DIV_C(x) UPDATE(x, 6, 5)
#define INNO_PRE_PLL_PCLK_DIV_D_MASK GENMASK(4, 0)
#define INNO_PRE_PLL_PCLK_DIV_D(x) UPDATE(x, 4, 0)
/* REG: 0xd1 */
#define INNO_PRE_PLL_FRAC_DIV_23_16(x) UPDATE((x) >> 16, 7, 0)
/* REG: 0xd2 */
#define INNO_PRE_PLL_FRAC_DIV_15_8(x) UPDATE((x) >> 8, 7, 0)
/* REG: 0xd3 */
#define INNO_PRE_PLL_FRAC_DIV_7_0(x) UPDATE(x, 7, 0)
/* REG: 0x1aa */
#define INNO_POST_PLL_POST_DIV_ENABLE GENMASK(3, 2)
#define INNO_POST_PLL_REFCLK_SEL_TMDS BIT(1)
#define INNO_POST_PLL_POWER_DOWN BIT(0)
#define INNO_POST_PLL_FB_DIV_8(x) UPDATE(((x) >> 8) <<4 , 4, 4)
/* REG:0x1ab */
#define INNO_POST_PLL_Pre_DIV_MASK GENMASK(5, 0)
#define INNO_POST_PLL_PRE_DIV(x) UPDATE(x, 5, 0)
/* REG: 0x1ac */
#define INNO_POST_PLL_FB_DIV_7_0(x) UPDATE(x, 7, 0)
/* REG: 0x1ad */
#define INNO_POST_PLL_POST_DIV_MASK GENMASK(2, 0)
#define INNO_POST_PLL_POST_DIV_2 0x0
#define INNO_POST_PLL_POST_DIV_4 0x1
#define INNO_POST_PLL_POST_DIV_8 0x3
/* REG: 0x1af */
#define INNO_POST_PLL_LOCK_STATUS BIT(0)
/* REG: 0x1b0 */
#define INNO_BANDGAP_ENABLE BIT(2)
/* REG: 0x1b2 */
#define INNO_TMDS_CLK_DRIVER_EN BIT(3)
#define INNO_TMDS_D2_DRIVER_EN BIT(2)
#define INNO_TMDS_D1_DRIVER_EN BIT(1)
#define INNO_TMDS_D0_DRIVER_EN BIT(0)
#define INNO_TMDS_DRIVER_ENABLE (INNO_TMDS_CLK_DRIVER_EN | \
INNO_TMDS_D2_DRIVER_EN | \
INNO_TMDS_D1_DRIVER_EN | \
INNO_TMDS_D0_DRIVER_EN)
/* REG:0x1c5 */
#define INNO_BYPASS_TERM_RESISTOR_CALIB BIT(7)
#define INNO_TERM_RESISTOR_CALIB_SPEED_14_8(x) UPDATE((x) >> 8, 6, 0)
/* REG:0x1c6 */
#define INNO_TERM_RESISTOR_CALIB_SPEED_7_0(x) UPDATE(x, 7, 0)
/* REG:0x1c7 */
#define INNO_TERM_RESISTOR_100 UPDATE(0, 2, 1)
#define INNO_TERM_RESISTOR_125 UPDATE(1, 2, 1)
#define INNO_TERM_RESISTOR_150 UPDATE(2, 2, 1)
#define INNO_TERM_RESISTOR_200 UPDATE(3, 2, 1)
/* REG 0x1c8 - 0x1cb */
#define INNO_ESD_DETECT_MASK GENMASK(5, 0)
#define INNO_ESD_DETECT_340MV (0x0 << 6)
#define INNO_ESD_DETECT_280MV (0x1 << 6)
#define INNO_ESD_DETECT_260MV (0x2 << 6)
#define INNO_ESD_DETECT_240MV (0x3 << 6)
/* resistors can be used in parallel */
#define INNO_TMDS_TERM_RESIST_MASK GENMASK(5, 0)
#define INNO_TMDS_TERM_RESIST_125 BIT(5)
#define INNO_TMDS_TERM_RESIST_250 BIT(4)
#define INNO_TMDS_TERM_RESIST_500 BIT(3)
#define INNO_TMDS_TERM_RESIST_1000 BIT(2)
#define INNO_TMDS_TERM_RESIST_2000 BIT(1)
#define INNO_TMDS_TERM_RESIST_4000 BIT(0)
struct pre_pll_config {
unsigned long pixclock;
unsigned long tmdsclock;
u8 prediv;
u16 fbdiv;
u8 tmds_div_a;
u8 tmds_div_b;
u8 tmds_div_c;
u8 pclk_div_a;
u8 pclk_div_b;
u8 pclk_div_c;
u8 pclk_div_d;
u8 vco_div_5_en;
u32 fracdiv;
};
struct post_pll_config {
unsigned long tmdsclock;
u8 prediv;
u16 fbdiv;
u8 postdiv;
u8 post_div_en;
u8 version;
};
struct phy_config {
unsigned long tmdsclock;
u8 regs[14];
};
typedef struct register_value {
u16 reg;
u8 value;
} reg_value_t;
struct hdmi_data_info {
int vic;
bool sink_is_hdmi;
bool sink_has_audio;
unsigned int enc_in_format;
unsigned int enc_out_format;
unsigned int colorimetry;
};
struct inno_hdmi {
struct device *dev;
struct drm_device *drm_dev;
int irq;
struct clk *pclk;
struct clk *sys_clk;
struct clk *mclk;
struct clk *bclk;
struct clk *phy_clk;
struct reset_control *tx_rst;
void __iomem *regs;
struct drm_connector connector;
struct drm_encoder encoder;
struct inno_hdmi_i2c *i2c;
struct i2c_adapter *ddc;
unsigned long tmds_rate;
struct gpio_desc *hpd_gpio;
struct hdmi_data_info hdmi_data;
struct drm_display_mode previous_mode;
struct regulator *hdmi_1p8;
struct regulator *hdmi_0p9;
const struct pre_pll_config *pre_cfg;
const struct post_pll_config *post_cfg;
};
int starfive_hdmi_audio_init(struct inno_hdmi *hdmi);
inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset);
inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, u32 val);
inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
u32 msk, u32 val);
#endif /* __INNO_HDMI_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,528 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 StarFive Technology Co., Ltd.
*
* reference to seeed5inch.c
*/
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/media-bus-format.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
#include "vs_drv.h"
#define RPI_DSI_DRIVER_NAME "cdns-dri-panel"
/* I2C registers of the Atmel microcontroller. */
enum REG_ADDR {
REG_ID = 0x80,
REG_PORTA, /* BIT(2) for horizontal flip, BIT(3) for vertical flip */
REG_PORTB,
REG_PORTC,
REG_PORTD,
REG_POWERON,
REG_PWM,
REG_DDRA,
REG_DDRB,
REG_DDRC,
REG_DDRD,
REG_TEST,
REG_WR_ADDRL,
REG_WR_ADDRH,
REG_READH,
REG_READL,
REG_WRITEH,
REG_WRITEL,
REG_ID2,
};
/* DSI D-PHY Layer Registers */
#define D0W_DPHYCONTTX 0x0004
#define CLW_DPHYCONTRX 0x0020
#define D0W_DPHYCONTRX 0x0024
#define D1W_DPHYCONTRX 0x0028
#define COM_DPHYCONTRX 0x0038
#define CLW_CNTRL 0x0040
#define D0W_CNTRL 0x0044
#define D1W_CNTRL 0x0048
#define DFTMODE_CNTRL 0x0054
/* DSI PPI Layer Registers */
#define PPI_STARTPPI 0x0104
#define PPI_BUSYPPI 0x0108
#define PPI_LINEINITCNT 0x0110
#define PPI_LPTXTIMECNT 0x0114
#define PPI_CLS_ATMR 0x0140
#define PPI_D0S_ATMR 0x0144
#define PPI_D1S_ATMR 0x0148
#define PPI_D0S_CLRSIPOCOUNT 0x0164
#define PPI_D1S_CLRSIPOCOUNT 0x0168
#define CLS_PRE 0x0180
#define D0S_PRE 0x0184
#define D1S_PRE 0x0188
#define CLS_PREP 0x01A0
#define D0S_PREP 0x01A4
#define D1S_PREP 0x01A8
#define CLS_ZERO 0x01C0
#define D0S_ZERO 0x01C4
#define D1S_ZERO 0x01C8
#define PPI_CLRFLG 0x01E0
#define PPI_CLRSIPO 0x01E4
#define HSTIMEOUT 0x01F0
#define HSTIMEOUTENABLE 0x01F4
/* DSI Protocol Layer Registers */
#define DSI_STARTDSI 0x0204
#define DSI_BUSYDSI 0x0208
#define DSI_LANEENABLE 0x0210
#define DSI_LANEENABLE_CLOCK BIT(0)
#define DSI_LANEENABLE_D0 BIT(1)
#define DSI_LANEENABLE_D1 BIT(2)
#define DSI_LANESTATUS0 0x0214
#define DSI_LANESTATUS1 0x0218
#define DSI_INTSTATUS 0x0220
#define DSI_INTMASK 0x0224
#define DSI_INTCLR 0x0228
#define DSI_LPTXTO 0x0230
#define DSI_MODE 0x0260
#define DSI_PAYLOAD0 0x0268
#define DSI_PAYLOAD1 0x026C
#define DSI_SHORTPKTDAT 0x0270
#define DSI_SHORTPKTREQ 0x0274
#define DSI_BTASTA 0x0278
#define DSI_BTACLR 0x027C
/* DSI General Registers */
#define DSIERRCNT 0x0300
#define DSISIGMOD 0x0304
/* DSI Application Layer Registers */
#define APLCTRL 0x0400
#define APLSTAT 0x0404
#define APLERR 0x0408
#define PWRMOD 0x040C
#define RDPKTLN 0x0410
#define PXLFMT 0x0414
#define MEMWRCMD 0x0418
/* LCDC/DPI Host Registers */
#define LCDCTRL 0x0420
#define HSR 0x0424
#define HDISPR 0x0428
#define VSR 0x042C
#define VDISPR 0x0430
#define VFUEN 0x0434
/* DBI-B Host Registers */
#define DBIBCTRL 0x0440
/* SPI Master Registers */
#define SPICMR 0x0450
#define SPITCR 0x0454
/* System Controller Registers */
#define SYSSTAT 0x0460
#define SYSCTRL 0x0464
#define SYSPLL1 0x0468
#define SYSPLL2 0x046C
#define SYSPLL3 0x0470
#define SYSPMCTRL 0x047C
/* GPIO Registers */
#define GPIOC 0x0480
#define GPIOO 0x0484
#define GPIOI 0x0488
/* I2C Registers */
#define I2CCLKCTRL 0x0490
/* Chip/Rev Registers */
#define IDREG 0x04A0
/* Debug Registers */
#define WCMDQUEUE 0x0500
#define RCMDQUEUE 0x0504
struct seeed_panel_dev {
struct i2c_client *client;
struct drm_panel base;
struct mipi_dsi_device *dsi;
struct device *dev;
int irq;
};
static int seeed_panel_i2c_write(struct i2c_client *client, u8 reg, u8 val)
{
struct i2c_msg msg;
u8 buf[2];
int ret;
buf[0] = reg;
buf[1] = val;
msg.addr = client->addr;
msg.flags = 0;
msg.buf = buf;
msg.len = 2;
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret >= 0)
return 0;
return ret;
}
static int seeed_panel_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
{
struct i2c_msg msg[2];
u8 buf[2];
int ret;
buf[0] = reg;
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].buf = buf;
msg[0].len = 1;
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = val;
msg[1].len = 1;
ret = i2c_transfer(client->adapter, msg, 2);
if (ret >= 0)
return 0;
return ret;
}
enum dsi_rgb_pattern_t {
RGB_PAT_WHITE,
RGB_PAT_BLACK,
RGB_PAT_RED,
RGB_PAT_GREEN,
RGB_PAT_BLUE,
RGB_PAT_HORIZ_COLORBAR,
RGB_PAT_VERT_COLORBAR,
RGB_PAT_NUM
};
static struct seeed_panel_dev *panel_to_seeed(struct drm_panel *panel)
{
return container_of(panel, struct seeed_panel_dev, base);
}
static const struct drm_display_mode seeed_panel_modes[] = {
#ifdef PLL_1228M
{
.clock = 27306666 / 1000,
.hdisplay = 800,
.hsync_start = 800 + 93,
.hsync_end = 800 + 93 + 5,
.htotal = 800 + 93 + 5 + 5,
.vdisplay = 480,
.vsync_start = 480 + 14,
.vsync_end = 480 + 14 + 5,
.vtotal = 480 + 14 + 5 + 5,
},
#endif
{// pll 1188M
.clock = 29700000 / 1000,
.hdisplay = 800,
.hsync_start = 800 + 90,
.hsync_end = 800 + 90 + 5,
.htotal = 800 + 90 + 5 + 5,
.vdisplay = 480,
.vsync_start = 480 + 60,
.vsync_end = 480 + 60 + 5,
.vtotal = 480 + 60 + 5 + 5,
},
};
static int seeed_panel_disable(struct drm_panel *panel)
{
struct seeed_panel_dev *sp = panel_to_seeed(panel);
seeed_panel_i2c_write(sp->client, REG_PWM, 0);
seeed_panel_i2c_write(sp->client, REG_POWERON, 0);
udelay(1);
return 0;
}
static int seeed_panel_noop(struct drm_panel *panel)
{
return 0;
}
static int seeed_dsi_write(struct drm_panel *panel, u16 reg, u32 val)
{
struct seeed_panel_dev *sp = panel_to_seeed(panel);
u8 msg[] = {
reg,
reg >> 8,
val,
val >> 8,
val >> 16,
val >> 24,
};
mipi_dsi_generic_write(sp->dsi, msg, sizeof(msg));
return 0;
}
static int seeed_panel_enable(struct drm_panel *panel)
{
struct seeed_panel_dev *sp = panel_to_seeed(panel);
int i;
u8 reg_value = 0;
seeed_panel_i2c_write(sp->client, REG_POWERON, 1);
/* Wait for nPWRDWN to go low to indicate poweron is done. */
for (i = 0; i < 100; i++) {
seeed_panel_i2c_read(sp->client, REG_PORTB, &reg_value);
if (reg_value & 1)
break;
}
seeed_dsi_write(panel, DSI_LANEENABLE,
DSI_LANEENABLE_CLOCK |
DSI_LANEENABLE_D0);
seeed_dsi_write(panel, PPI_D0S_CLRSIPOCOUNT, 0x05);
seeed_dsi_write(panel, PPI_D1S_CLRSIPOCOUNT, 0x05);
seeed_dsi_write(panel, PPI_D0S_ATMR, 0x00);
seeed_dsi_write(panel, PPI_D1S_ATMR, 0x00);
seeed_dsi_write(panel, PPI_LPTXTIMECNT, 0x03);
seeed_dsi_write(panel, SPICMR, 0x00);
seeed_dsi_write(panel, LCDCTRL, 0x00100150);
seeed_dsi_write(panel, SYSCTRL, 0x040f);
msleep(100);
seeed_dsi_write(panel, PPI_STARTPPI, 0x01);
seeed_dsi_write(panel, DSI_STARTDSI, 0x01);
msleep(100);
/* Turn on the backlight. */
seeed_panel_i2c_write(sp->client, REG_PWM, 255);
/* Default to the same orientation as the closed source
* firmware used for the panel. Runtime rotation
* configuration will be supported using VC4's plane
* orientation bits.
*/
seeed_panel_i2c_write(sp->client, REG_PORTA, BIT(2));
return 0;
}
static int seeed_panel_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
unsigned int i, num = 0;
static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
for (i = 0; i < ARRAY_SIZE(seeed_panel_modes); i++) {
const struct drm_display_mode *m = &seeed_panel_modes[i];
struct drm_display_mode *mode;
mode = drm_mode_duplicate(connector->dev, m);
if (!mode) {
dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
m->hdisplay, m->vdisplay,
drm_mode_vrefresh(m));
continue;
}
mode->type |= DRM_MODE_TYPE_DRIVER;
if (i == 0)
mode->type |= DRM_MODE_TYPE_PREFERRED;
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode);
num++;
}
connector->display_info.bpc = 8;
connector->display_info.width_mm = 154;
connector->display_info.height_mm = 86;
drm_display_info_set_bus_formats(&connector->display_info,
&bus_format, 1);
return num;
}
static const struct drm_panel_funcs seeed_panel_funcs = {
.disable = seeed_panel_disable,
.unprepare = seeed_panel_noop,
.prepare = seeed_panel_noop,
.enable = seeed_panel_enable,
.get_modes = seeed_panel_get_modes,
};
static int seeed_panel_probe(struct i2c_client *client)
{
u8 reg_value = 0;
struct seeed_panel_dev *seeed_panel;
struct device_node *endpoint, *dsi_host_node;
struct mipi_dsi_host *host;
struct device *dev = &client->dev;
struct mipi_dsi_device_info info = {
.type = RPI_DSI_DRIVER_NAME,
.channel = 0, //0,
.node = NULL,
};
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_warn(&client->dev,
"I2C adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
return -EIO;
}
seeed_panel = devm_kzalloc(&client->dev, sizeof(struct seeed_panel_dev), GFP_KERNEL);
if (!seeed_panel)
return -ENOMEM;
seeed_panel->client = client;
i2c_set_clientdata(client, seeed_panel);
seeed_panel_i2c_read(client, REG_ID, &reg_value);
switch (reg_value) {
case 0xde: /* ver 1 */
case 0xc3: /* ver 2 */
break;
default:
dev_err(&client->dev, "Unknown Atmel firmware revision: 0x%02x\n", reg_value);
return -ENODEV;
}
seeed_panel_i2c_write(client, REG_POWERON, 0);
endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
if (!endpoint)
return -ENODEV;
dsi_host_node = of_graph_get_remote_port_parent(endpoint);
if (!dsi_host_node)
goto error;
host = of_find_mipi_dsi_host_by_node(dsi_host_node);
of_node_put(dsi_host_node);
if (!host) {
of_node_put(endpoint);
return -EPROBE_DEFER;
}
drm_panel_init(&seeed_panel->base, dev, &seeed_panel_funcs,
DRM_MODE_CONNECTOR_DSI);
/* This appears last, as it's what will unblock the DSI host
* driver's component bind function.
*/
drm_panel_add(&seeed_panel->base);
info.node = of_node_get(of_graph_get_remote_port(endpoint));
if (!info.node)
goto error;
of_node_put(endpoint);
seeed_panel->dsi = mipi_dsi_device_register_full(host, &info);
if (IS_ERR(seeed_panel->dsi)) {
dev_err(dev, "DSI device registration failed: %ld\n",
PTR_ERR(seeed_panel->dsi));
return PTR_ERR(seeed_panel->dsi);
}
return 0;
error:
of_node_put(endpoint);
return -ENODEV;
}
static void seeed_panel_remove(struct i2c_client *client)
{
struct seeed_panel_dev *seeed_panel = i2c_get_clientdata(client);
mipi_dsi_detach(seeed_panel->dsi);
drm_panel_remove(&seeed_panel->base);
mipi_dsi_device_unregister(seeed_panel->dsi);
// kfree(seeed_panel->dsi);
//return 0;
}
static const struct i2c_device_id seeed_panel_id[] = {
{ "seeed_panel", 0 },
{ }
};
static const struct of_device_id seeed_panel_dt_ids[] = {
{ .compatible = "seeed_panel", },
{ /* sentinel */ }
};
static struct i2c_driver seeed_panel_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "seeed_panel",
.of_match_table = seeed_panel_dt_ids,
},
.probe = seeed_panel_probe,
.remove = seeed_panel_remove,
.id_table = seeed_panel_id,
};
static int seeed_dsi_probe(struct mipi_dsi_device *dsi)
{
int ret;
dsi->mode_flags = (MIPI_DSI_MODE_VIDEO |
MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
MIPI_DSI_MODE_LPM);
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->lanes = 1;
ret = mipi_dsi_attach(dsi);
if (ret)
dev_err(&dsi->dev, "failed to attach dsi to host: %d\n", ret);
return ret;
}
static struct mipi_dsi_driver seeed_dsi_driver = {
.driver.name = RPI_DSI_DRIVER_NAME,
.probe = seeed_dsi_probe,
};
static int __init init_seeed_panel(void)
{
int err;
mipi_dsi_driver_register(&seeed_dsi_driver);
err = i2c_add_driver(&seeed_panel_driver);
return err;
}
module_init(init_seeed_panel);
static void __exit exit_seeed_panel(void)
{
i2c_del_driver(&seeed_panel_driver);
mipi_dsi_driver_unregister(&seeed_dsi_driver);
}
module_exit(exit_seeed_panel);
MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
MODULE_DESCRIPTION("Raspberry Pi 7-inch touchscreen driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,293 @@
// SPDX-License-Identifier: GPL-2.0
/*
* HDMI Audio driver for the StarFive JH7110 SoC
*
* Copyright (C) 2022 StarFive Technology Co., Ltd.
* Author: Xingyu Wu <xingyu.wu@starfivetech.com>
*/
#include <linux/device.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include <sound/pcm_params.h>
#include <linux/of_platform.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include "inno_hdmi.h"
#define SF_PCM_RATE_32000_192000 (SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_88200 | \
SNDRV_PCM_RATE_96000 | \
SNDRV_PCM_RATE_176400 | \
SNDRV_PCM_RATE_192000)
#define DEV_MUTE 0x005
#define AUDIO_CFG 0x035
#define SAMPLE_FRE 0x037
#define PINS_ENA 0x038
#define CHANNEL_INPUT 0x039
#define N_VALUE1 0x03F
#define N_VALUE2 0x040
#define N_VALUE3 0x041
#define CTS_VALUE1 0x045
#define CTS_VALUE2 0x046
#define CTS_VALUE3 0x047
/* DEV_MUTE */
#define AUDIO_MUTE_MASK BIT(1)
#define AUDIO_MUTE BIT(1)
#define AUDIO_NO_MUTE 0x0
/* AUDIO_CFG */
#define MCLK_RATIO_MASK GENMASK(1, 0)
#define MCLK_128FS 0x0
#define MCLK_256FS 0x1
#define MCLK_384FS 0x2
#define MCLK_512FS 0x3
#define AUDIO_TYPE_SEL_MASK GENMASK(4, 3)
#define AUDIO_SEL_I2S 0x0
#define AUDIO_SEL_SPDIF BIT(3)
#define CTS_SOURCE_SEL_MASK BIT(7)
#define CTS_INTER 0x0
#define CTS_EXTER BIT(7)
/* SAMPLE_FRE */
#define I2S_SAMP_FREQ_MASK GENMASK(3, 0)
#define FREQ_32K 0x3
#define FREQ_44K 0x0
#define FREQ_48K 0x2
#define FREQ_88K 0x8
#define FREQ_96K 0xa
#define FREQ_176K 0xc
#define FREQ_192K 0xe
/* PINS_ENA */
#define I2S_FORMAT_MASK GENMASK(1, 0)
#define STANDARD_MODE 0x0
#define RIGHT_JUSTIFIED_MODE 0x1
#define LEFT_JUSTIFIED_MODE 0x2
#define I2S_PIN_ENA_MASK GENMASK(5, 2)
#define I2S0_ENA BIT(2)
#define I2S1_ENA BIT(3)
#define I2S2_ENA BIT(4)
#define I2S3_ENA BIT(5)
/* CHANNEL_INPUT */
#define CHANNEL0_INPUT_MASK GENMASK(1, 0)
#define CHANNEL0_I2S0 (0x0 << 0)
#define CHANNEL0_I2S3 (0x1 << 0)
#define CHANNEL0_I2S2 (0x2 << 0)
#define CHANNEL0_I2S1 (0x3 << 0)
#define CHANNEL1_INPUT_MASK GENMASK(3, 2)
#define CHANNEL1_I2S1 (0x0 << 2)
#define CHANNEL1_I2S0 (0x1 << 2)
#define CHANNEL1_I2S3 (0x2 << 2)
#define CHANNEL1_I2S2 (0x3 << 2)
#define CHANNEL2_INPUT_MASK GENMASK(5, 4)
#define CHANNEL2_I2S2 (0x0 << 4)
#define CHANNEL2_I2S1 (0x1 << 4)
#define CHANNEL2_I2S0 (0x2 << 4)
#define CHANNEL2_I2S3 (0x3 << 4)
#define CHANNEL3_INPUT_MASK GENMASK(7, 6)
#define CHANNEL3_I2S3 (0x0 << 6)
#define CHANNEL3_I2S2 (0x1 << 6)
#define CHANNEL3_I2S1 (0x2 << 6)
#define CHANNEL3_I2S0 (0x3 << 6)
static int starfive_hdmi_audio_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
struct inno_hdmi *priv = snd_soc_dai_get_drvdata(dai);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
/* Audio no mute */
hdmi_modb(priv, DEV_MUTE, AUDIO_MUTE_MASK, AUDIO_NO_MUTE);
return 0;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
/* Audio mute */
hdmi_modb(priv, DEV_MUTE, AUDIO_MUTE_MASK, AUDIO_MUTE);
return 0;
default:
return -EINVAL;
}
}
static int starfive_hdmi_audio_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct inno_hdmi *priv = snd_soc_dai_get_drvdata(dai);
unsigned int sample_rate;
unsigned int channels;
unsigned int rate_reg;
unsigned int channels_reg;
unsigned int Nvalue;
unsigned int CTSvalue;
unsigned int TMDS = priv->tmds_rate;
dev_dbg(priv->dev, "HDMI&AUDIO: tmds rate:%d\n", TMDS);
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
return 0;
sample_rate = params_rate(params);
switch (sample_rate) {
case 32000:
rate_reg = FREQ_32K;
break;
case 44100:
rate_reg = FREQ_44K;
break;
case 48000:
rate_reg = FREQ_48K;
break;
case 88200:
rate_reg = FREQ_88K;
break;
case 96000:
rate_reg = FREQ_96K;
break;
case 176400:
rate_reg = FREQ_176K;
break;
case 192000:
rate_reg = FREQ_192K;
break;
default:
dev_err(priv->dev, "HDMI&AUDIO: not support sample rate:%d\n",
sample_rate);
return -EINVAL;
}
Nvalue = 128 * sample_rate / 1000;
CTSvalue = TMDS / 1000;
channels = params_channels(params);
switch (channels) {
case 2:
channels_reg = I2S0_ENA;
break;
case 4:
channels_reg = I2S0_ENA | I2S1_ENA;
break;
case 6:
channels_reg = I2S0_ENA | I2S1_ENA | I2S2_ENA;
break;
case 8:
channels_reg = I2S0_ENA | I2S1_ENA | I2S2_ENA | I2S3_ENA;
break;
default:
dev_err(priv->dev, "HDMI&AUDIO: not support channels:%d\n",
channels);
return -EINVAL;
}
hdmi_modb(priv, AUDIO_CFG, CTS_SOURCE_SEL_MASK, CTS_EXTER);
hdmi_writeb(priv, SAMPLE_FRE, rate_reg);
hdmi_modb(priv, PINS_ENA, I2S_PIN_ENA_MASK, channels_reg);
/* N{reg3f[3:0],reg40[7:0],reg41[7:0]} */
hdmi_writeb(priv, N_VALUE1, ((Nvalue >> 16) & 0xf));
hdmi_writeb(priv, N_VALUE2, ((Nvalue >> 8) & 0xff));
hdmi_writeb(priv, N_VALUE3, ((Nvalue >> 0) & 0xff));
/* CTS{reg45[3:0],reg46[7:0],reg47[7:0]} */
hdmi_writeb(priv, CTS_VALUE1, ((CTSvalue >> 16) & 0xf));
hdmi_writeb(priv, CTS_VALUE2, ((CTSvalue >> 8) & 0xff));
hdmi_writeb(priv, CTS_VALUE3, ((CTSvalue >> 0) & 0xff));
dev_dbg(priv->dev, "HDMI&AUDIO: AUDIO_CFG :0x%x\n",
hdmi_readb(priv, AUDIO_CFG));
dev_dbg(priv->dev, "HDMI&AUDIO: CHANNEL_INPUT :0x%x\n",
hdmi_readb(priv, CHANNEL_INPUT));
dev_dbg(priv->dev, "HDMI&AUDIO: SAMPLE_FRE :0x%x\n",
hdmi_readb(priv, SAMPLE_FRE));
dev_dbg(priv->dev, "HDMI&AUDIO: PINS_ENA :0x%x\n",
hdmi_readb(priv, PINS_ENA));
dev_dbg(priv->dev, "HDMI&AUDIO: N_VALUE :0x%x, 0x%x, 0x%x\n",
hdmi_readb(priv, N_VALUE1),
hdmi_readb(priv, N_VALUE2),
hdmi_readb(priv, N_VALUE3));
dev_dbg(priv->dev, "HDMI&AUDIO: CTS_VALUE :0x%x,0x%x,0x%x\n",
hdmi_readb(priv, CTS_VALUE1),
hdmi_readb(priv, CTS_VALUE2),
hdmi_readb(priv, CTS_VALUE3));
return 0;
}
static int starfive_hdmi_audio_probe(struct snd_soc_component *component)
{
/* In this time, HDMI has suspend and cannot read and write register. */
return 0;
}
static const struct snd_soc_dai_ops starfive_hdmi_audio_dai_ops = {
.trigger = starfive_hdmi_audio_trigger,
.hw_params = starfive_hdmi_audio_hw_params,
};
static struct snd_soc_dai_driver starfive_hdmi_audio_dai = {
.name = "starfive-hdmi-audio",
.id = 0,
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
.rates = SF_PCM_RATE_32000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &starfive_hdmi_audio_dai_ops,
.symmetric_rate = 1,
};
static const struct snd_soc_component_driver starfive_hdmi_audio_component = {
.name = "starfive-hdmi-audio",
.probe = starfive_hdmi_audio_probe,
};
int starfive_hdmi_audio_init(struct inno_hdmi *hdmi)
{
int ret;
ret = devm_snd_soc_register_component(hdmi->dev, &starfive_hdmi_audio_component,
&starfive_hdmi_audio_dai, 1);
if (ret) {
dev_err(hdmi->dev, "HDMI&AUDIO: not able to register dai\n");
return ret;
}
/* Use external CTS source */
hdmi_modb(hdmi, AUDIO_CFG, CTS_SOURCE_SEL_MASK, CTS_EXTER);
/* select I2S type */
hdmi_modb(hdmi, AUDIO_CFG, AUDIO_TYPE_SEL_MASK, AUDIO_SEL_I2S);
/* MCLK ratio 0:128fs, 1:256fs, 2:384fs, 3:512fs */
hdmi_modb(hdmi, AUDIO_CFG, MCLK_RATIO_MASK, MCLK_256FS);
/* I2S format 0:standard, 1:right-justified, 2:left-justified */
hdmi_modb(hdmi, PINS_ENA, I2S_FORMAT_MASK, STANDARD_MODE);
/* Audio channel input */
hdmi_writeb(hdmi, CHANNEL_INPUT, CHANNEL0_I2S0 | CHANNEL1_I2S1 |
CHANNEL2_I2S2 | CHANNEL3_I2S3);
/* Audio mute */
hdmi_modb(hdmi, DEV_MUTE, AUDIO_MUTE_MASK, AUDIO_MUTE);
dev_info(hdmi->dev, "HDMI&AUDIO register done.\n");
return 0;
}

View file

@ -0,0 +1,470 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/media-bus-format.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_crtc.h>
#include <drm/vs_drm.h>
#include "vs_crtc.h"
#if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE
#include <drm/drm_vblank.h>
#endif
#define CONFIG_ENABLE_GAMMA_LUT 0
void vs_crtc_destroy(struct drm_crtc *crtc)
{
struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
drm_crtc_cleanup(crtc);
kfree(vs_crtc);
}
static void vs_crtc_reset(struct drm_crtc *crtc)
{
struct vs_crtc_state *state;
if (crtc->state) {
__drm_atomic_helper_crtc_destroy_state(crtc->state);
state = to_vs_crtc_state(crtc->state);
kfree(state);
crtc->state = NULL;
}
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (state == NULL)
return;
__drm_atomic_helper_crtc_reset(crtc, &state->base);
state->sync_mode = VS_SINGLE_DC;
state->output_fmt = MEDIA_BUS_FMT_RBG888_1X24;
state->encoder_type = DRM_MODE_ENCODER_NONE;
#ifdef CONFIG_VERISILICON_MMU
state->mmu_prefetch = VS_MMU_PREFETCH_DISABLE;
#endif
}
static struct drm_crtc_state *
vs_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
{
struct vs_crtc_state *ori_state;
struct vs_crtc_state *state;
if (WARN_ON(!crtc->state))
return NULL;
ori_state = to_vs_crtc_state(crtc->state);
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return NULL;
__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
state->sync_mode = ori_state->sync_mode;
state->output_fmt = ori_state->output_fmt;
state->encoder_type = ori_state->encoder_type;
state->bg_color = ori_state->bg_color;
state->bpp = ori_state->bpp;
state->sync_enable = ori_state->sync_enable;
state->dither_enable = ori_state->dither_enable;
state->underflow = ori_state->underflow;
#ifdef CONFIG_VERISILICON_MMU
state->mmu_prefetch = ori_state->mmu_prefetch;
#endif
return &state->base;
}
static void vs_crtc_atomic_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
__drm_atomic_helper_crtc_destroy_state(state);
kfree(to_vs_crtc_state(state));
}
static int vs_crtc_atomic_set_property(struct drm_crtc *crtc,
struct drm_crtc_state *state,
struct drm_property *property,
uint64_t val)
{
struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(state);
if (property == vs_crtc->sync_mode)
vs_crtc_state->sync_mode = val;
else if (property == vs_crtc->mmu_prefetch)
vs_crtc_state->mmu_prefetch = val;
else if (property == vs_crtc->bg_color)
vs_crtc_state->bg_color = val;
else if (property == vs_crtc->panel_sync)
vs_crtc_state->sync_enable = val;
else if (property == vs_crtc->dither)
vs_crtc_state->dither_enable = val;
else
return -EINVAL;
return 0;
}
static int vs_crtc_atomic_get_property(struct drm_crtc *crtc,
const struct drm_crtc_state *state,
struct drm_property *property,
uint64_t *val)
{
struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
const struct vs_crtc_state *vs_crtc_state =
container_of(state, const struct vs_crtc_state, base);
if (property == vs_crtc->sync_mode)
*val = vs_crtc_state->sync_mode;
else if (property == vs_crtc->mmu_prefetch)
*val = vs_crtc_state->mmu_prefetch;
else if (property == vs_crtc->bg_color)
*val = vs_crtc_state->bg_color;
else if (property == vs_crtc->panel_sync)
*val = vs_crtc_state->sync_enable;
else if (property == vs_crtc->dither)
*val = vs_crtc_state->dither_enable;
else
return -EINVAL;
return 0;
}
#ifdef CONFIG_DEBUG_FS
static int vs_crtc_debugfs_show(struct seq_file *s, void *data)
{
struct drm_crtc *crtc = s->private;
struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state);
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
seq_printf(s, "crtc[%u]: %s\n", crtc->base.id, crtc->name);
seq_printf(s, "\tactive = %d\n", crtc->state->active);
seq_printf(s, "\tsize = %dx%d\n", mode->hdisplay, mode->vdisplay);
seq_printf(s, "\tbpp = %u\n", crtc_state->bpp);
seq_printf(s, "\tunderflow = %d\n", crtc_state->underflow);
return 0;
}
static int vs_crtc_debugfs_open(struct inode *inode, struct file *file)
{
return single_open(file, vs_crtc_debugfs_show, inode->i_private);
}
static const struct file_operations vs_crtc_debugfs_fops = {
.open = vs_crtc_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int vs_crtc_debugfs_init(struct drm_crtc *crtc)
{
debugfs_create_file("status", 0444, crtc->debugfs_entry,
crtc, &vs_crtc_debugfs_fops);
return 0;
}
#else
static int vs_crtc_debugfs_init(struct drm_crtc *crtc)
{
return 0;
}
#endif /* CONFIG_DEBUG_FS */
static int vs_crtc_late_register(struct drm_crtc *crtc)
{
return vs_crtc_debugfs_init(crtc);
}
static int vs_crtc_enable_vblank(struct drm_crtc *crtc)
{
struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
u32 ctrc_mask = 0;
ctrc_mask = drm_crtc_mask(crtc);
vs_crtc->funcs->enable_vblank(vs_crtc->dev, true, ctrc_mask);
return 0;
}
static void vs_crtc_disable_vblank(struct drm_crtc *crtc)
{
struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
u32 ctrc_mask = 0;
ctrc_mask = drm_crtc_mask(crtc);
vs_crtc->funcs->enable_vblank(vs_crtc->dev, false, ctrc_mask);
}
static const struct drm_crtc_funcs vs_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.destroy = vs_crtc_destroy,
.page_flip = drm_atomic_helper_page_flip,
.reset = vs_crtc_reset,
.atomic_duplicate_state = vs_crtc_atomic_duplicate_state,
.atomic_destroy_state = vs_crtc_atomic_destroy_state,
.atomic_set_property = vs_crtc_atomic_set_property,
.atomic_get_property = vs_crtc_atomic_get_property,
//.gamma_set = drm_atomic_helper_legacy_gamma_set,
.late_register = vs_crtc_late_register,
.enable_vblank = vs_crtc_enable_vblank,
.disable_vblank = vs_crtc_disable_vblank,
};
static u8 cal_pixel_bits(u32 bus_format)
{
u8 bpp;
switch (bus_format) {
case MEDIA_BUS_FMT_RGB565_1X16:
case MEDIA_BUS_FMT_UYVY8_1X16:
bpp = 16;
break;
case MEDIA_BUS_FMT_RGB666_1X18:
case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
bpp = 18;
break;
case MEDIA_BUS_FMT_UYVY10_1X20:
bpp = 20;
break;
case MEDIA_BUS_FMT_BGR888_1X24:
case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
case MEDIA_BUS_FMT_YUV8_1X24:
bpp = 24;
break;
case MEDIA_BUS_FMT_RGB101010_1X30:
case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
case MEDIA_BUS_FMT_YUV10_1X30:
bpp = 30;
break;
default:
bpp = 24;
break;
}
return bpp;
}
static bool vs_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
return vs_crtc->funcs->mode_fixup(vs_crtc->dev, mode, adjusted_mode);
}
static void vs_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
vs_crtc_state->bpp = cal_pixel_bits(vs_crtc_state->output_fmt);
vs_crtc->funcs->enable(vs_crtc->dev, crtc);
drm_crtc_vblank_on(crtc);
}
static void vs_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
drm_crtc_vblank_off(crtc);
vs_crtc->funcs->disable(vs_crtc->dev, crtc);
if (crtc->state->event && !crtc->state->active) {
spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_send_vblank_event(crtc, crtc->state->event);
spin_unlock_irq(&crtc->dev->event_lock);
crtc->state->event = NULL;
}
}
static void vs_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
crtc);
//struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,crtc);
struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
struct device *dev = vs_crtc->dev;
struct drm_property_blob *blob = crtc->state->gamma_lut;
struct drm_color_lut *lut;
if (crtc_state->color_mgmt_changed) {
if ((blob) && (blob->length)) {
lut = blob->data;
vs_crtc->funcs->set_gamma(dev, crtc, lut,
blob->length / sizeof(*lut));
vs_crtc->funcs->enable_gamma(dev, crtc, true);
} else {
vs_crtc->funcs->enable_gamma(dev, crtc, false);
}
}
}
static void vs_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
struct drm_pending_vblank_event *event = crtc->state->event;
vs_crtc->funcs->commit(vs_crtc->dev);
if (event) {
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_arm_vblank_event(crtc, event);
spin_unlock_irq(&crtc->dev->event_lock);
crtc->state->event = NULL;
}
}
static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = {
.mode_fixup = vs_crtc_mode_fixup,
.atomic_enable = vs_crtc_atomic_enable,
.atomic_disable = vs_crtc_atomic_disable,
.atomic_begin = vs_crtc_atomic_begin,
.atomic_flush = vs_crtc_atomic_flush,
};
static const struct drm_prop_enum_list vs_sync_mode_enum_list[] = {
{ VS_SINGLE_DC, "single dc mode" },
{ VS_MULTI_DC_PRIMARY, "primary dc for multi dc mode" },
{ VS_MULTI_DC_SECONDARY, "secondary dc for multi dc mode" },
};
#ifdef CONFIG_VERISILICON_MMU
static const struct drm_prop_enum_list vs_mmu_prefetch_enum_list[] = {
{ VS_MMU_PREFETCH_DISABLE, "disable mmu prefetch" },
{ VS_MMU_PREFETCH_ENABLE, "enable mmu prefetch" },
};
#endif
struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
struct vs_dc_info *info)
{
struct vs_crtc *crtc;
int ret;
if (!info)
return NULL;
crtc = kzalloc(sizeof(struct vs_crtc), GFP_KERNEL);
if (!crtc)
return NULL;
ret = drm_crtc_init_with_planes(drm_dev, &crtc->base,
NULL, NULL, &vs_crtc_funcs,
info->name ? info->name : NULL);
if (ret)
goto err_free_crtc;
drm_crtc_helper_add(&crtc->base, &vs_crtc_helper_funcs);
/* Set up the crtc properties */
if (info->pipe_sync) {
crtc->sync_mode = drm_property_create_enum(drm_dev, 0,
"SYNC_MODE",
vs_sync_mode_enum_list,
ARRAY_SIZE(vs_sync_mode_enum_list));
if (!crtc->sync_mode)
goto err_cleanup_crts;
drm_object_attach_property(&crtc->base.base,
crtc->sync_mode,
VS_SINGLE_DC);
}
#if CONFIG_ENABLE_GAMMA_LUT
if (info->gamma_size) {
ret = drm_mode_crtc_set_gamma_size(&crtc->base,
info->gamma_size);
if (ret)
goto err_cleanup_crts;
drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
info->gamma_size);
}
#endif
if (info->background) {
crtc->bg_color = drm_property_create_range(drm_dev, 0,
"BG_COLOR", 0, 0xffffffff);
if (!crtc->bg_color)
goto err_cleanup_crts;
drm_object_attach_property(&crtc->base.base, crtc->bg_color, 0);
}
if (info->panel_sync) {
crtc->panel_sync = drm_property_create_bool(drm_dev, 0, "SYNC_ENABLED");
if (!crtc->panel_sync)
goto err_cleanup_crts;
drm_object_attach_property(&crtc->base.base, crtc->panel_sync, 0);
}
crtc->dither = drm_property_create_bool(drm_dev, 0, "DITHER_ENABLED");
if (!crtc->dither)
goto err_cleanup_crts;
drm_object_attach_property(&crtc->base.base, crtc->dither, 0);
#ifdef CONFIG_VERISILICON_MMU
if (info->mmu_prefetch) {
crtc->mmu_prefetch = drm_property_create_enum(drm_dev, 0,
"MMU_PREFETCH",
vs_mmu_prefetch_enum_list,
ARRAY_SIZE(vs_mmu_prefetch_enum_list));
if (!crtc->mmu_prefetch)
goto err_cleanup_crts;
drm_object_attach_property(&crtc->base.base,
crtc->mmu_prefetch,
VS_MMU_PREFETCH_DISABLE);
}
#endif
crtc->max_bpc = info->max_bpc;
crtc->color_formats = info->color_formats;
return crtc;
err_cleanup_crts:
drm_crtc_cleanup(&crtc->base);
err_free_crtc:
kfree(crtc);
return NULL;
}
void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow)
{
struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc->state);
drm_crtc_handle_vblank(crtc);
vs_crtc_state->underflow = underflow;
}

View file

@ -0,0 +1,74 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#ifndef __VS_CRTC_H__
#define __VS_CRTC_H__
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include "vs_type.h"
struct vs_crtc_funcs {
void (*enable)(struct device *dev, struct drm_crtc *crtc);
void (*disable)(struct device *dev, struct drm_crtc *crtc);
bool (*mode_fixup)(struct device *dev,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
void (*set_gamma)(struct device *dev, struct drm_crtc *crtc,
struct drm_color_lut *lut, unsigned int size);
void (*enable_gamma)(struct device *dev, struct drm_crtc *crtc,
bool enable);
void (*enable_vblank)(struct device *dev, bool enable, u32 ctrc_mask);
void (*commit)(struct device *dev);
};
struct vs_crtc_state {
struct drm_crtc_state base;
u32 sync_mode;
u32 output_fmt;
u32 bg_color;
u8 encoder_type;
u8 mmu_prefetch;
u8 bpp;
bool sync_enable;
bool dither_enable;
bool underflow;
};
struct vs_crtc {
struct drm_crtc base;
struct device *dev;
struct drm_pending_vblank_event *event;
unsigned int max_bpc;
unsigned int color_formats; /* supported color format */
struct drm_property *sync_mode;
struct drm_property *mmu_prefetch;
struct drm_property *bg_color;
struct drm_property *panel_sync;
struct drm_property *dither;
const struct vs_crtc_funcs *funcs;
};
void vs_crtc_destroy(struct drm_crtc *crtc);
struct vs_crtc *vs_crtc_create(struct drm_device *drm_dev,
struct vs_dc_info *info);
void vs_crtc_handle_vblank(struct drm_crtc *crtc, bool underflow);
static inline struct vs_crtc *to_vs_crtc(struct drm_crtc *crtc)
{
return container_of(crtc, struct vs_crtc, base);
}
static inline struct vs_crtc_state *
to_vs_crtc_state(struct drm_crtc_state *state)
{
return container_of(state, struct vs_crtc_state, base);
}
#endif /* __VS_CRTC_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,91 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#ifndef __VS_DC_H__
#define __VS_DC_H__
#include <linux/version.h>
#include <linux/mm_types.h>
#include <drm/drm_modes.h>
#if KERNEL_VERSION(5, 5, 0) > LINUX_VERSION_CODE
#include <drm/drmP.h>
#endif
#include "vs_plane.h"
#include "vs_crtc.h"
#include "vs_dc_hw.h"
#include "vs_dc_dec.h"
#ifdef CONFIG_VERISILICON_MMU
#include "vs_dc_mmu.h"
#endif
struct vs_dc_funcs {
void (*dump_enable)(struct device *dev, dma_addr_t addr,
unsigned int pitch);
void (*dump_disable)(struct device *dev);
};
struct vs_dc_plane {
enum dc_hw_plane_id id;
};
struct vs_dc {
struct vs_crtc *crtc[DC_DISPLAY_NUM];
struct dc_hw hw;
#ifdef CONFIG_VERISILICON_DEC
struct dc_dec400l dec400l;
#endif
bool first_frame;
struct vs_dc_plane planes[PLANE_NUM];
const struct vs_dc_funcs *funcs;
struct clk *cpu_axi;
struct clk *axicfg0_axi;
struct clk *disp_axi;
struct clk *stg_axi;
struct clk *vout_src;
struct clk *vout_axi;
struct clk *ahb1;
struct clk *vout_ahb;
struct clk *hdmitx0_mclk;
struct clk *bclk_mst;
struct clk *dc8200_clk_pix0;
struct clk *dc8200_clk_pix1;
struct clk *dc8200_axi;
struct clk *dc8200_core;
struct clk *dc8200_ahb;
struct clk *vout_top_axi;
struct clk *vout_top_lcd;
struct clk *hdmitx0_pixelclk;
struct clk *dc8200_pix0;
struct reset_control *vout_resets;
struct reset_control *dc8200_rst_axi;
struct reset_control *dc8200_rst_core;
struct reset_control *dc8200_rst_ahb;
struct reset_control *rst_vout_src;
struct reset_control *noc_disp;
struct regmap *dss_regmap;
int init_count;
};
extern struct platform_driver dc_platform_driver;
extern void sifive_l2_flush64_range(unsigned long start, unsigned long len);
void vs_dc_update_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
struct drm_plane *drm_plane,
struct drm_atomic_state *drm_state);
void vs_dc_disable_cursor_plane(struct vs_dc *dc, struct vs_plane *plane,
struct drm_plane_state *old_state);
int vs_dc_check_cursor_plane(struct vs_dc *dc, struct drm_plane *plane,
struct drm_atomic_state *state);
#endif /* __VS_DC_H__ */

View file

@ -0,0 +1,386 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include "vs_dc_dec.h"
#define fourcc_mod_vs_get_tile_mode(val) ((u8)((val) & \
DRM_FORMAT_MOD_VS_DEC_TILE_MODE_MASK))
static inline bool _is_stream_changed(struct dc_dec400l *dec400l, u8 stream_id)
{
return dec400l->stream[stream_id].dirty;
}
static inline bool _is_stream_valid(struct dc_dec400l *dec400l, u8 stream_id)
{
return !!(dec400l->stream[stream_id].main_base_addr);
}
static void _enable_stream(struct dc_dec400l *dec400l, struct dc_hw *hw,
u8 stream_id)
{
if (!(dec400l->stream_status & (1 << stream_id))) {
if (!(dec400l->stream_status))
/* the first enabled steram */
dc_hw_dec_init(hw);
dec400l->stream_status |= 1 << stream_id;
}
}
static void _disable_stream(struct dc_dec400l *dec400l, struct dc_hw *hw,
u8 stream_id)
{
if ((dec400l->stream_status & (1 << stream_id)))
dec400l->stream_status &= ~(1 << stream_id);
}
static u16 get_dec_tile_size(u8 tile_mode, u8 cpp)
{
u16 multi = 0;
switch (tile_mode) {
case DRM_FORMAT_MOD_VS_DEC_RASTER_16X1:
multi = 16;
break;
case DRM_FORMAT_MOD_VS_DEC_TILE_8X4:
case DRM_FORMAT_MOD_VS_DEC_TILE_4X8:
case DRM_FORMAT_MOD_VS_DEC_RASTER_32X1:
case DRM_FORMAT_MOD_VS_DEC_TILE_8X4_S:
multi = 32;
break;
case DRM_FORMAT_MOD_VS_DEC_TILE_8X8_XMAJOR:
case DRM_FORMAT_MOD_VS_DEC_TILE_8X8_YMAJOR:
case DRM_FORMAT_MOD_VS_DEC_TILE_16X4:
case DRM_FORMAT_MOD_VS_DEC_RASTER_16X4:
case DRM_FORMAT_MOD_VS_DEC_RASTER_64X1:
case DRM_FORMAT_MOD_VS_DEC_RASTER_32X2:
case DRM_FORMAT_MOD_VS_DEC_TILE_16X4_S:
case DRM_FORMAT_MOD_VS_DEC_TILE_16X4_LSB:
multi = 64;
break;
case DRM_FORMAT_MOD_VS_DEC_TILE_32X4:
case DRM_FORMAT_MOD_VS_DEC_RASTER_128X1:
case DRM_FORMAT_MOD_VS_DEC_TILE_16X8:
case DRM_FORMAT_MOD_VS_DEC_TILE_8X16:
case DRM_FORMAT_MOD_VS_DEC_RASTER_32X4:
case DRM_FORMAT_MOD_VS_DEC_RASTER_64X2:
case DRM_FORMAT_MOD_VS_DEC_TILE_32X4_S:
case DRM_FORMAT_MOD_VS_DEC_TILE_32X4_LSB:
multi = 128;
break;
case DRM_FORMAT_MOD_VS_DEC_TILE_64X4:
case DRM_FORMAT_MOD_VS_DEC_RASTER_256X1:
case DRM_FORMAT_MOD_VS_DEC_RASTER_64X4:
case DRM_FORMAT_MOD_VS_DEC_RASTER_128X2:
case DRM_FORMAT_MOD_VS_DEC_TILE_16X16:
case DRM_FORMAT_MOD_VS_DEC_TILE_32X8:
multi = 256;
break;
case DRM_FORMAT_MOD_VS_DEC_RASTER_256X2:
case DRM_FORMAT_MOD_VS_DEC_RASTER_128X4:
case DRM_FORMAT_MOD_VS_DEC_RASTER_512X1:
case DRM_FORMAT_MOD_VS_DEC_TILE_128X4:
case DRM_FORMAT_MOD_VS_DEC_TILE_32X16:
multi = 512;
break;
case DRM_FORMAT_MOD_VS_DEC_TILE_256X4:
case DRM_FORMAT_MOD_VS_DEC_TILE_64X16:
case DRM_FORMAT_MOD_VS_DEC_TILE_128X8:
multi = 1024;
break;
case DRM_FORMAT_MOD_VS_DEC_TILE_512X4:
multi = 2048;
break;
default:
break;
}
return multi * cpp;
}
static void update_fb_format(struct dc_dec_fb *dec_fb)
{
struct drm_framebuffer *drm_fb = dec_fb->fb;
u8 tile_mod = fourcc_mod_vs_get_tile_mode(drm_fb->modifier);
u8 norm_mod = DRM_FORMAT_MOD_VS_LINEAR;
switch (tile_mod) {
case DRM_FORMAT_MOD_VS_DEC_RASTER_32X1:
norm_mod = DRM_FORMAT_MOD_VS_TILE_32X1;
break;
case DRM_FORMAT_MOD_VS_DEC_RASTER_64X1:
norm_mod = DRM_FORMAT_MOD_VS_TILE_64X1;
break;
case DRM_FORMAT_MOD_VS_DEC_RASTER_128X1:
norm_mod = DRM_FORMAT_MOD_VS_TILE_128X1;
break;
case DRM_FORMAT_MOD_VS_DEC_RASTER_256X1:
norm_mod = DRM_FORMAT_MOD_VS_TILE_256X1;
break;
case DRM_FORMAT_MOD_VS_DEC_TILE_8X4:
norm_mod = DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR_8X4;
break;
case DRM_FORMAT_MOD_VS_DEC_TILE_4X8:
norm_mod = DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR_4X8;
break;
case DRM_FORMAT_MOD_VS_DEC_TILE_8X8_XMAJOR:
if (drm_fb->format->format == DRM_FORMAT_YUYV ||
drm_fb->format->format == DRM_FORMAT_UYVY ||
drm_fb->format->format == DRM_FORMAT_P010)
norm_mod = DRM_FORMAT_MOD_VS_TILE_8X8;
else
norm_mod = DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR;
break;
case DRM_FORMAT_MOD_VS_DEC_TILE_16X8:
if (drm_fb->format->format == DRM_FORMAT_NV12 ||
drm_fb->format->format == DRM_FORMAT_P010)
norm_mod = DRM_FORMAT_MOD_VS_TILE_8X8;
break;
case DRM_FORMAT_MOD_VS_DEC_TILE_32X8:
if (drm_fb->format->format == DRM_FORMAT_NV12)
norm_mod = DRM_FORMAT_MOD_VS_TILE_8X8;
break;
default:
break;
}
drm_fb->modifier = fourcc_mod_vs_norm_code(norm_mod);
}
static void _stream_config(struct dc_dec_fb *dec_fb,
struct dc_dec_stream *stream, u8 index)
{
struct drm_framebuffer *drm_fb = dec_fb->fb;
const struct drm_format_info *info = drm_fb->format;
u32 plane_height = drm_format_info_plane_height(info,
drm_fb->height, index);
u16 tile_size;
stream->main_base_addr = dec_fb->addr[index];
stream->tile_mode = fourcc_mod_vs_get_tile_mode(drm_fb->modifier);
if (drm_fb->modifier & DRM_FORMAT_MOD_VS_DEC_ALIGN_32)
stream->align_mode = DEC_ALIGN_32;
else
stream->align_mode = DEC_ALIGN_64;
switch (drm_fb->format->format) {
case DRM_FORMAT_ARGB8888:
stream->format = DEC_FORMAT_ARGB8;
stream->depth = DEC_DEPTH_8;
break;
case DRM_FORMAT_XRGB8888:
stream->format = DEC_FORMAT_XRGB8;
stream->depth = DEC_DEPTH_8;
break;
case DRM_FORMAT_RGB565:
stream->format = DEC_FORMAT_R5G6B5;
stream->depth = DEC_DEPTH_8;
break;
case DRM_FORMAT_ARGB1555:
stream->format = DEC_FORMAT_A1RGB5;
stream->depth = DEC_DEPTH_8;
break;
case DRM_FORMAT_XRGB1555:
stream->format = DEC_FORMAT_X1RGB5;
stream->depth = DEC_DEPTH_8;
break;
case DRM_FORMAT_ARGB4444:
stream->format = DEC_FORMAT_ARGB4;
stream->depth = DEC_DEPTH_8;
break;
case DRM_FORMAT_XRGB4444:
stream->format = DEC_FORMAT_XRGB4;
stream->depth = DEC_DEPTH_8;
break;
case DRM_FORMAT_ARGB2101010:
stream->format = DEC_FORMAT_A2R10G10B10;
stream->depth = DEC_DEPTH_10;
break;
case DRM_FORMAT_YUYV:
stream->format = DEC_FORMAT_YUY2;
stream->depth = DEC_DEPTH_8;
break;
case DRM_FORMAT_UYVY:
stream->format = DEC_FORMAT_UYVY;
stream->depth = DEC_DEPTH_8;
break;
case DRM_FORMAT_YVU420:
stream->format = DEC_FORMAT_YUV_ONLY;
stream->depth = DEC_DEPTH_8;
break;
case DRM_FORMAT_NV12:
WARN_ON(stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_256X1 &&
stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_128X1 &&
stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_TILE_32X8 &&
stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_TILE_16X8);
if (index) {
stream->format = DEC_FORMAT_UV_MIX;
switch (stream->tile_mode) {
case DRM_FORMAT_MOD_VS_DEC_RASTER_256X1:
stream->tile_mode =
DRM_FORMAT_MOD_VS_DEC_RASTER_128X1;
break;
case DRM_FORMAT_MOD_VS_DEC_RASTER_128X1:
stream->tile_mode =
DRM_FORMAT_MOD_VS_DEC_RASTER_64X1;
break;
case DRM_FORMAT_MOD_VS_DEC_TILE_32X8:
stream->tile_mode =
DRM_FORMAT_MOD_VS_DEC_TILE_32X4;
break;
case DRM_FORMAT_MOD_VS_DEC_TILE_16X8:
stream->tile_mode =
DRM_FORMAT_MOD_VS_DEC_TILE_16X4;
break;
default:
break;
}
} else {
stream->format = DEC_FORMAT_YUV_ONLY;
}
stream->depth = DEC_DEPTH_8;
break;
case DRM_FORMAT_P010:
WARN_ON(stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_128X1 &&
stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_64X1 &&
stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_TILE_16X8 &&
stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_TILE_8X8_XMAJOR);
if (index) {
stream->format = DEC_FORMAT_UV_MIX;
switch (stream->tile_mode) {
case DRM_FORMAT_MOD_VS_DEC_RASTER_128X1:
stream->tile_mode =
DRM_FORMAT_MOD_VS_DEC_RASTER_64X1;
break;
case DRM_FORMAT_MOD_VS_DEC_RASTER_64X1:
stream->tile_mode =
DRM_FORMAT_MOD_VS_DEC_RASTER_32X1;
break;
case DRM_FORMAT_MOD_VS_DEC_TILE_16X8:
stream->tile_mode =
DRM_FORMAT_MOD_VS_DEC_TILE_16X4;
break;
case DRM_FORMAT_MOD_VS_DEC_TILE_8X8_XMAJOR:
stream->tile_mode =
DRM_FORMAT_MOD_VS_DEC_TILE_8X4;
break;
default:
break;
}
} else {
stream->format = DEC_FORMAT_YUV_ONLY;
}
stream->depth = DEC_DEPTH_10;
break;
case DRM_FORMAT_NV16:
WARN_ON(stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_256X1 &&
stream->tile_mode != DRM_FORMAT_MOD_VS_DEC_RASTER_128X1);
if (index) {
stream->format = DEC_FORMAT_UV_MIX;
switch (stream->tile_mode) {
case DRM_FORMAT_MOD_VS_DEC_RASTER_256X1:
stream->tile_mode =
DRM_FORMAT_MOD_VS_DEC_RASTER_128X1;
break;
case DRM_FORMAT_MOD_VS_DEC_RASTER_128X1:
stream->tile_mode =
DRM_FORMAT_MOD_VS_DEC_RASTER_64X1;
break;
default:
break;
}
} else {
stream->format = DEC_FORMAT_YUV_ONLY;
}
stream->depth = DEC_DEPTH_8;
break;
}
tile_size = get_dec_tile_size(stream->tile_mode, info->cpp[index]);
stream->aligned_stride = ALIGN(dec_fb->stride[index], tile_size);
stream->ts_base_addr = stream->main_base_addr +
stream->aligned_stride * plane_height;
}
static int _dec400l_config(struct dc_dec400l *dec400l,
struct dc_dec_fb *dec_fb, u8 stream_base)
{
struct dc_dec_stream stream;
u8 i, stream_id, num_planes = 0;
if (dec_fb) {
const struct drm_format_info *info = dec_fb->fb->format;
num_planes = info->num_planes;
}
for (i = 0; i < STREAM_COUNT; i++) {
stream_id = stream_base + i;
memset(&dec400l->stream[stream_id], 0, sizeof(struct dc_dec_stream));
if (i < num_planes) {
memset(&stream, 0, sizeof(struct dc_dec_stream));
_stream_config(dec_fb, &stream, i);
memcpy(&dec400l->stream[stream_id], &stream,
sizeof(struct dc_dec_stream));
}
dec400l->stream[stream_id].dirty = true;
}
if (dec_fb)
update_fb_format(dec_fb);
return 0;
}
int dc_dec_config(struct dc_dec400l *dec400l, struct dc_dec_fb *dec_fb,
u8 stream_base)
{
if (dec_fb && !dec_fb->fb)
return -EINVAL;
if (dec_fb && (fourcc_mod_vs_get_type(dec_fb->fb->modifier) !=
DRM_FORMAT_MOD_VS_TYPE_COMPRESSED))
_dec400l_config(dec400l, NULL, stream_base);
else
_dec400l_config(dec400l, dec_fb, stream_base);
return 0;
}
int dc_dec_commit(struct dc_dec400l *dec400l, struct dc_hw *hw)
{
u8 i;
for (i = 0; i < STREAM_TOTAL; i++) {
if (!_is_stream_changed(dec400l, i))
continue;
if (_is_stream_valid(dec400l, i)) {
_enable_stream(dec400l, hw, i);
dc_hw_dec_stream_set(hw, dec400l->stream[i].main_base_addr,
dec400l->stream[i].ts_base_addr, dec400l->stream[i].tile_mode,
dec400l->stream[i].align_mode, dec400l->stream[i].format,
dec400l->stream[i].depth, i);
} else {
dc_hw_dec_stream_disable(hw, i);
_disable_stream(dec400l, hw, i);
}
dec400l->stream[i].dirty = false;
}
return 0;
}

View file

@ -0,0 +1,106 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#ifndef _VS_DC_DEC_H_
#define _VS_DC_DEC_H_
#include <drm/drm_fourcc.h>
#include "vs_dc_hw.h"
#define fourcc_mod_vs_get_type(val) \
(((val) & DRM_FORMAT_MOD_VS_TYPE_MASK) >> 54)
/* DEC_READ_CONFIG */
#define COMPRESSION_EN BIT(0)
#define COMPRESSION_FORMAT_MASK GENMASK(7, 3)
#define COMPRESSION_ALIGN_MODE_MASK GENMASK(17, 16)
#define TILE_MODE_MASK GENMASK(30, 25)
/* DEC_READ_EX_CONFIG */
#define BIT_DEPTH_MASK GENMASK(18, 16)
/* DEC_CONTROL */
#define FLUSH_ENABLE BIT(0)
#define COMPRESSION_DISABLE BIT(1)
/* DEC_CONTROL_EX */
#define WRITE_MISS_POLICY1 1
#define WRITE_MISS_POLICY_MASK BIT(19)
#define READ_MISS_POLICY0 0
#define READ_MISS_POLICY_MASK BIT(29)
/* DEC_CONTROL_EX2 */
#define TILE_STATUS_READ_ID 16
#define TILE_STATUS_READ_ID_MASK GENMASK(6, 0)
#define TILE_STATUS_READ_ID_H 0
#define TILE_STATUS_READ_ID_H_MASK GENMASK(23, 22)
#define DISABLE_HW_DEC_FLUSH BIT(28)
#define STREAM_COUNT 3
#define STREAM_TOTAL 32
#define DEC_PLANE_MAX 3
enum dc_dec_align_mode {
DEC_ALIGN_32 = 0x02,
DEC_ALIGN_64,
};
enum dc_dec_format {
DEC_FORMAT_ARGB8,
DEC_FORMAT_XRGB8,
DEC_FORMAT_AYUV,
DEC_FORMAT_UYVY,
DEC_FORMAT_YUY2,
DEC_FORMAT_YUV_ONLY,
DEC_FORMAT_UV_MIX,
DEC_FORMAT_ARGB4,
DEC_FORMAT_XRGB4,
DEC_FORMAT_A1RGB5,
DEC_FORMAT_X1RGB5,
DEC_FORMAT_R5G6B5,
DEC_FORMAT_A2R10G10B10 = 0x0F,
DEC_FORMAT_BAYER,
DEC_FORMAT_COEFFICIENT = 0x12,
DEC_FORMAT_ARGB16,
DEC_FORMAT_X2RGB10 = 0x15,
};
enum dc_dec_depth {
DEC_DEPTH_8,
DEC_DEPTH_10,
DEC_DEPTH_12,
DEC_DEPTH_14,
DEC_DEPTH_16,
};
struct dc_dec_stream {
u32 main_base_addr;
u32 aligned_stride;
u32 ts_base_addr;
u8 tile_mode;
u8 align_mode;
u8 format;
u8 depth;
bool dirty;
};
struct dc_dec_fb {
struct drm_framebuffer *fb;
u32 addr[DEC_PLANE_MAX];
u32 stride[DEC_PLANE_MAX];
};
struct dc_dec400l {
struct dc_dec_stream stream[STREAM_TOTAL];
u32 stream_status;
};
int dc_dec_config(struct dc_dec400l *dec400l, struct dc_dec_fb *dec_fb,
u8 stream_base);
int dc_dec_commit(struct dc_dec400l *dec400l, struct dc_hw *hw);
#endif /* _VS_DC_DEC_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,580 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#ifndef __VS_DC_HW_H__
#define __VS_DC_HW_H__
#include <linux/version.h>
#if KERNEL_VERSION(5, 5, 0) > LINUX_VERSION_CODE
#include <drm/drmP.h>
#endif
#ifdef CONFIG_VERISILICON_MMU
#include "vs_dc_mmu.h"
#endif
#define AQ_INTR_ACKNOWLEDGE 0x0010
#define AQ_INTR_ENBL 0x0014
#define DC_HW_REVISION 0x0024
#define DC_HW_CHIP_CID 0x0030
#define DC_REG_BASE 0x0800
#define DC_REG_RANGE 0x2000
#define DC_SEC_REG_OFFSET 0x100000
#define DC_FRAMEBUFFER_CONFIG 0x1518
#define DC_FRAMEBUFFER_CONFIG_EX 0x1CC0
#define DC_FRAMEBUFFER_SCALE_CONFIG 0x1520
#define DC_FRAMEBUFFER_TOP_LEFT 0x24D8
#define DC_FRAMEBUFFER_BOTTOM_RIGHT 0x24E0
#define DC_FRAMEBUFFER_ADDRESS 0x1400
#define DC_FRAMEBUFFER_U_ADDRESS 0x1530
#define DC_FRAMEBUFFER_V_ADDRESS 0x1538
#define DC_FRAMEBUFFER_STRIDE 0x1408
#define DC_FRAMEBUFFER_U_STRIDE 0x1800
#define DC_FRAMEBUFFER_V_STRIDE 0x1808
#define DC_FRAMEBUFFER_SIZE 0x1810
#define DC_FRAMEBUFFER_SCALE_FACTOR_X 0x1828
#define DC_FRAMEBUFFER_SCALE_FACTOR_Y 0x1830
#define DC_FRAMEBUFFER_H_FILTER_COEF_INDEX 0x1838
#define DC_FRAMEBUFFER_H_FILTER_COEF_DATA 0x1A00
#define DC_FRAMEBUFFER_V_FILTER_COEF_INDEX 0x1A08
#define DC_FRAMEBUFFER_V_FILTER_COEF_DATA 0x1A10
#define DC_FRAMEBUFFER_INIT_OFFSET 0x1A20
#define DC_FRAMEBUFFER_COLOR_KEY 0x1508
#define DC_FRAMEBUFFER_COLOR_KEY_HIGH 0x1510
#define DC_FRAMEBUFFER_CLEAR_VALUE 0x1A18
#define DC_FRAMEBUFFER_COLOR_TABLE_INDEX 0x1818
#define DC_FRAMEBUFFER_COLOR_TABLE_DATA 0x1820
#define DC_FRAMEBUFFER_BG_COLOR 0x1528
#define DC_FRAMEBUFFER_ROI_ORIGIN 0x1CB0
#define DC_FRAMEBUFFER_ROI_SIZE 0x1CB8
#define DC_FRAMEBUFFER_WATER_MARK 0x1CE8
#define DC_FRAMEBUFFER_DEGAMMA_INDEX 0x1D88
#define DC_FRAMEBUFFER_DEGAMMA_DATA 0x1D90
#define DC_FRAMEBUFFER_DEGAMMA_EX_DATA 0x1D98
#define DC_FRAMEBUFFER_YUVTORGB_COEF0 0x1DA0
#define DC_FRAMEBUFFER_YUVTORGB_COEF1 0x1DA8
#define DC_FRAMEBUFFER_YUVTORGB_COEF2 0x1DB0
#define DC_FRAMEBUFFER_YUVTORGB_COEF3 0x1DB8
#define DC_FRAMEBUFFER_YUVTORGB_COEF4 0x1E00
#define DC_FRAMEBUFFER_YUVTORGB_COEFD0 0x1E08
#define DC_FRAMEBUFFER_YUVTORGB_COEFD1 0x1E10
#define DC_FRAMEBUFFER_YUVTORGB_COEFD2 0x1E18
#define DC_FRAMEBUFFER_Y_CLAMP_BOUND 0x1E88
#define DC_FRAMEBUFFER_UV_CLAMP_BOUND 0x1E90
#define DC_FRAMEBUFFER_RGBTORGB_COEF0 0x1E20
#define DC_FRAMEBUFFER_RGBTORGB_COEF1 0x1E28
#define DC_FRAMEBUFFER_RGBTORGB_COEF2 0x1E30
#define DC_FRAMEBUFFER_RGBTORGB_COEF3 0x1E38
#define DC_FRAMEBUFFER_RGBTORGB_COEF4 0x1E40
#define DC_FRAMEBUFFER_BLEND_CONFIG 0x2510
#define DC_FRAMEBUFFER_SRC_GLOBAL_COLOR 0x2500
#define DC_FRAMEBUFFER_DST_GLOBAL_COLOR 0x2508
#define DC_OVERLAY_CONFIG 0x1540
#define DC_OVERLAY_CONFIG_EX 0x2540
#define DC_OVERLAY_SCALE_CONFIG 0x1C00
#define DC_OVERLAY_BLEND_CONFIG 0x1580
#define DC_OVERLAY_TOP_LEFT 0x1640
#define DC_OVERLAY_BOTTOM_RIGHT 0x1680
#define DC_OVERLAY_ADDRESS 0x15C0
#define DC_OVERLAY_U_ADDRESS 0x1840
#define DC_OVERLAY_V_ADDRESS 0x1880
#define DC_OVERLAY_STRIDE 0x1600
#define DC_OVERLAY_U_STRIDE 0x18C0
#define DC_OVERLAY_V_STRIDE 0x1900
#define DC_OVERLAY_SIZE 0x17C0
#define DC_OVERLAY_SCALE_FACTOR_X 0x1A40
#define DC_OVERLAY_SCALE_FACTOR_Y 0x1A80
#define DC_OVERLAY_H_FILTER_COEF_INDEX 0x1AC0
#define DC_OVERLAY_H_FILTER_COEF_DATA 0x1B00
#define DC_OVERLAY_V_FILTER_COEF_INDEX 0x1B40
#define DC_OVERLAY_V_FILTER_COEF_DATA 0x1B80
#define DC_OVERLAY_INIT_OFFSET 0x1BC0
#define DC_OVERLAY_COLOR_KEY 0x1740
#define DC_OVERLAY_COLOR_KEY_HIGH 0x1780
#define DC_OVERLAY_CLEAR_VALUE 0x1940
#define DC_OVERLAY_COLOR_TABLE_INDEX 0x1980
#define DC_OVERLAY_COLOR_TABLE_DATA 0x19C0
#define DC_OVERLAY_SRC_GLOBAL_COLOR 0x16C0
#define DC_OVERLAY_DST_GLOBAL_COLOR 0x1700
#define DC_OVERLAY_ROI_ORIGIN 0x1D00
#define DC_OVERLAY_ROI_SIZE 0x1D40
#define DC_OVERLAY_WATER_MARK 0x1DC0
#define DC_OVERLAY_DEGAMMA_INDEX 0x2200
#define DC_OVERLAY_DEGAMMA_DATA 0x2240
#define DC_OVERLAY_DEGAMMA_EX_DATA 0x2280
#define DC_OVERLAY_YUVTORGB_COEF0 0x1EC0
#define DC_OVERLAY_YUVTORGB_COEF1 0x1F00
#define DC_OVERLAY_YUVTORGB_COEF2 0x1F40
#define DC_OVERLAY_YUVTORGB_COEF3 0x1F80
#define DC_OVERLAY_YUVTORGB_COEF4 0x1FC0
#define DC_OVERLAY_YUVTORGB_COEFD0 0x2000
#define DC_OVERLAY_YUVTORGB_COEFD1 0x2040
#define DC_OVERLAY_YUVTORGB_COEFD2 0x2080
#define DC_OVERLAY_Y_CLAMP_BOUND 0x22C0
#define DC_OVERLAY_UV_CLAMP_BOUND 0x2300
#define DC_OVERLAY_RGBTORGB_COEF0 0x20C0
#define DC_OVERLAY_RGBTORGB_COEF1 0x2100
#define DC_OVERLAY_RGBTORGB_COEF2 0x2140
#define DC_OVERLAY_RGBTORGB_COEF3 0x2180
#define DC_OVERLAY_RGBTORGB_COEF4 0x21C0
#define DC_CURSOR_CONFIG 0x1468
#define DC_CURSOR_ADDRESS 0x146C
#define DC_CURSOR_LOCATION 0x1470
#define DC_CURSOR_BACKGROUND 0x1474
#define DC_CURSOR_FOREGROUND 0x1478
#define DC_CURSOR_CLK_GATING 0x1484
#define DC_CURSOR_CONFIG_EX 0x24E8
#define DC_CURSOR_OFFSET 0x1080
#define DC_DISPLAY_DITHER_CONFIG 0x1410
#define DC_DISPLAY_PANEL_CONFIG 0x1418
#define DC_DISPLAY_PANEL_CONFIG_EX 0x2518
#define DC_DISPLAY_DITHER_TABLE_LOW 0x1420
#define DC_DISPLAY_DITHER_TABLE_HIGH 0x1428
#define DC_DISPLAY_H 0x1430
#define DC_DISPLAY_H_SYNC 0x1438
#define DC_DISPLAY_V 0x1440
#define DC_DISPLAY_V_SYNC 0x1448
#define DC_DISPLAY_CURRENT_LOCATION 0x1450
#define DC_DISPLAY_GAMMA_INDEX 0x1458
#define DC_DISPLAY_GAMMA_DATA 0x1460
#define DC_DISPLAY_INT 0x147C
#define DC_DISPLAY_INT_ENABLE 0x1480
#define DC_DISPLAY_DBI_CONFIG 0x1488
#define DC_DISPLAY_GENERAL_CONFIG 0x14B0
#define DC_DISPLAY_DPI_CONFIG 0x14B8
#define DC_DISPLAY_PANEL_START 0x1CCC
#define DC_DISPLAY_DEBUG_COUNTER_SELECT 0x14D0
#define DC_DISPLAY_DEBUG_COUNTER_VALUE 0x14D8
#define DC_DISPLAY_DP_CONFIG 0x1CD0
#define DC_DISPLAY_GAMMA_EX_INDEX 0x1CF0
#define DC_DISPLAY_GAMMA_EX_DATA 0x1CF8
#define DC_DISPLAY_GAMMA_EX_ONE_DATA 0x1D80
#define DC_DISPLAY_RGBTOYUV_COEF0 0x1E48
#define DC_DISPLAY_RGBTOYUV_COEF1 0x1E50
#define DC_DISPLAY_RGBTOYUV_COEF2 0x1E58
#define DC_DISPLAY_RGBTOYUV_COEF3 0x1E60
#define DC_DISPLAY_RGBTOYUV_COEF4 0x1E68
#define DC_DISPLAY_RGBTOYUV_COEFD0 0x1E70
#define DC_DISPLAY_RGBTOYUV_COEFD1 0x1E78
#define DC_DISPLAY_RGBTOYUV_COEFD2 0x1E80
#define DC_CLK_GATTING 0x1A28
#define DC_QOS_CONFIG 0x1A38
#define DC_TRANSPARENCY_OPAQUE 0x00
#define DC_TRANSPARENCY_KEY 0x02
#define DC_DISPLAY_DITHERTABLE_LOW 0x7B48F3C0
#define DC_DISPLAY_DITHERTABLE_HIGH 0x596AD1E2
#define GAMMA_SIZE 256
#define GAMMA_EX_SIZE 300
#define DEGAMMA_SIZE 260
#define RGB_TO_RGB_TABLE_SIZE 9
#define YUV_TO_RGB_TABLE_SIZE 16
#define RGB_TO_YUV_TABLE_SIZE 12
#ifdef CONFIG_VERISILICON_DEC
/* DEC400 register */
#define DEC_CONTROL 0x0800
#define DEC_CONTROL_EX 0x0804
#define DEC_CONTROL_EX2 0x0808
#define DEC_READ_CONFIG 0x0880
#define DEC_READ_EX_CONFIG 0x0900
#define DEC_READ_BUFFER_BASE 0x0A80
#define DEC_READ_BUFFER_END 0x0B80
#define DEC_READ_CACHE_BASE 0x1080
#define DEC_CONTROL_RESET 0x0301018A
#define DEC_CONTROL_EX_RESET 0x00080000
#define DEC_CONTROL_EX2_RESET 0x103FC810
#define DEC_READ_CONFIG_RESET 0x00020000
#endif
#ifdef CONFIG_VERISILICON_MMU
#define DC_MMU_PREFETCH 0x1E98
#define MMU_REG_BASE 0x0180
#define MMU_REG_RANGE 0x700
#define MMU_REG_CONFIG 0x0184
#define MMU_REG_CONTROL 0x0388
#define MMU_REG_TABLE_ARRAY_SIZE 0x0394
#define MMU_REG_SAFE_NON_SECURE 0x0398
#define MMU_REG_SAFE_SECURE 0x039C
#define MMU_REG_SAFE_EXT_ADDRESS 0x03A0
#define MMU_REG_CONTEXT_PD 0x03B4
#define DEC_REG_CONTROL 0x0800
#define DEC_REG_CONTROL_VALUE 0x02010188
#define SE_MMU_REG_BASE 0x10010
#define SE_MMU_REG_RANGE 0x60
#define SE_MMU_REG_CONFIG 0x10010
#define SE_MMU_REG_CONTROL 0x10024
#define SE_MMU_REG_TABLE_ARRAY_SIZE 0x10030
#define SE_MMU_REG_SAFE_NON_SECUR 0x10034
#define SE_MMU_REG_SAFE_SECURE 0x10038
#define SE_MMU_REG_SAFE_EXT_ADDRESS 0x1003C
#define SE_MMU_REG_CONTEXT_PD 0x10040
#define SE_MMU_REG_INTR_ENBL 0x10044
#endif
#define DC_LAYER_NUM 6
#define DC_DISPLAY_NUM 2
#define DC_CURSOR_NUM 2
enum dc_chip_rev {
DC_REV_0,/* For HW_REV_5720,HW_REV_5721_311 */
DC_REV_1,/* For HW_REV_5721_30B */
DC_REV_2,/* For HW_REV_5721_310 */
};
enum dc_hw_plane_id {
PRIMARY_PLANE_0,
OVERLAY_PLANE_0,
OVERLAY_PLANE_1,
PRIMARY_PLANE_1,
OVERLAY_PLANE_2,
OVERLAY_PLANE_3,
CURSOR_PLANE_0,
CURSOR_PLANE_1,
PLANE_NUM
};
enum dc_hw_color_format {
FORMAT_X4R4G4B4,//0
FORMAT_A4R4G4B4,//1
FORMAT_X1R5G5B5,//2
FORMAT_A1R5G5B5,//3
FORMAT_R5G6B5,//4
FORMAT_X8R8G8B8,//5
FORMAT_A8R8G8B8,//6
FORMAT_YUY2,//7
FORMAT_UYVY,//8
FORMAT_INDEX8,//9
FORMAT_MONOCHROME,//10
FORMAT_YV12 = 0xf,
FORMAT_A8,//16
FORMAT_NV12,//17
FORMAT_NV16,//18
FORMAT_RG16,//19
FORMAT_R8,//20
FORMAT_NV12_10BIT,//21
FORMAT_A2R10G10B10,//22
FORMAT_NV16_10BIT,//23
FORMAT_INDEX1,//24
FORMAT_INDEX2,//25
FORMAT_INDEX4,//26
FORMAT_P010,//27
FORMAT_YUV444,//28
FORMAT_YUV444_10BIT,//29
};
enum dc_hw_yuv_color_space {
COLOR_SPACE_601 = 0,
COLOR_SPACE_709 = 1,
COLOR_SPACE_2020 = 3,
};
enum dc_hw_rotation {
ROT_0 = 0,
ROT_90 = 4,
ROT_180 = 5,
ROT_270 = 6,
FLIP_X = 1,
FLIP_Y = 2,
FLIP_XY = 3,
};
enum dc_hw_swizzle {
SWIZZLE_ARGB = 0,
SWIZZLE_RGBA,
SWIZZLE_ABGR,
SWIZZLE_BGRA,
};
enum dc_hw_out {
OUT_DPI,
OUT_DP,
};
enum dc_hw_cursor_size {
CURSOR_SIZE_32X32 = 0,
CURSOR_SIZE_64X64,
};
enum dc_hw_blend_mode {
/* out.rgb = plane_alpha * fg.rgb +
* (1 - (plane_alpha * fg.alpha)) * bg.rgb
*/
BLEND_PREMULTI,
/* out.rgb = plane_alpha * fg.alpha * fg.rgb +
* (1 - (plane_alpha * fg.alpha)) * bg.rgb
*/
BLEND_COVERAGE,
/* out.rgb = plane_alpha * fg.rgb +
* (1 - plane_alpha) * bg.rgb
*/
BLEND_PIXEL_NONE,
};
struct dc_hw_plane_reg {
u32 y_address;
u32 u_address;
u32 v_address;
u32 y_stride;
u32 u_stride;
u32 v_stride;
u32 size;
u32 top_left;
u32 bottom_right;
u32 scale_factor_x;
u32 scale_factor_y;
u32 h_filter_coef_index;
u32 h_filter_coef_data;
u32 v_filter_coef_index;
u32 v_filter_coef_data;
u32 init_offset;
u32 color_key;
u32 color_key_high;
u32 clear_value;
u32 color_table_index;
u32 color_table_data;
u32 scale_config;
u32 water_mark;
u32 degamma_index;
u32 degamma_data;
u32 degamma_ex_data;
u32 src_global_color;
u32 dst_global_color;
u32 blend_config;
u32 roi_origin;
u32 roi_size;
u32 YUVToRGBCoef0;
u32 YUVToRGBCoef1;
u32 YUVToRGBCoef2;
u32 YUVToRGBCoef3;
u32 YUVToRGBCoef4;
u32 YUVToRGBCoefD0;
u32 YUVToRGBCoefD1;
u32 YUVToRGBCoefD2;
u32 YClampBound;
u32 UVClampBound;
u32 RGBToRGBCoef0;
u32 RGBToRGBCoef1;
u32 RGBToRGBCoef2;
u32 RGBToRGBCoef3;
u32 RGBToRGBCoef4;
};
#ifdef CONFIG_VERISILICON_MMU
struct dc_hw_mmu_reg {
u32 mmu_config;
u32 mmu_control;
u32 table_array_size;
u32 safe_non_secure;
u32 safe_secure;
u32 safe_ex;
u32 context_pd_entry;
};
#endif
struct dc_hw_fb {
u32 y_address;
u32 u_address;
u32 v_address;
u32 clear_value;
u32 water_mark;
u16 y_stride;
u16 u_stride;
u16 v_stride;
u16 width;
u16 height;
u8 format;
u8 tile_mode;
u8 rotation;
u8 yuv_color_space;
u8 swizzle;
u8 uv_swizzle;
u8 zpos;
u8 display_id;
bool clear_enable;
bool dec_enable;
bool enable;
bool dirty;
};
struct dc_hw_scale {
u32 scale_factor_x;
u32 scale_factor_y;
bool enable;
bool dirty;
};
struct dc_hw_position {
u16 start_x;
u16 start_y;
u16 end_x;
u16 end_y;
bool dirty;
};
struct dc_hw_blend {
u8 alpha;
u8 blend_mode;
bool dirty;
};
struct dc_hw_colorkey {
u32 colorkey;
u32 colorkey_high;
u8 transparency;
bool dirty;
};
struct dc_hw_roi {
u16 x;
u16 y;
u16 width;
u16 height;
bool enable;
bool dirty;
};
struct dc_hw_cursor {
u32 address;
u16 x;
u16 y;
u16 hot_x;
u16 hot_y;
u8 size;
u8 display_id;
bool enable;
bool dirty;
};
struct dc_hw_display {
u32 bus_format;
u16 h_active;
u16 h_total;
u16 h_sync_start;
u16 h_sync_end;
u16 v_active;
u16 v_total;
u16 v_sync_start;
u16 v_sync_end;
u16 sync_mode;
u32 bg_color;
u8 id;
bool h_sync_polarity;
bool v_sync_polarity;
bool enable;
bool sync_enable;
bool dither_enable;
};
struct dc_hw_gamma {
u16 gamma[GAMMA_EX_SIZE][3];
bool enable;
bool dirty;
};
struct dc_hw_degamma {
u16 degamma[DEGAMMA_SIZE][3];
u32 mode;
bool dirty;
};
struct dc_hw_plane {
struct dc_hw_fb fb;
struct dc_hw_position pos;
struct dc_hw_scale scale;
struct dc_hw_blend blend;
struct dc_hw_roi roi;
struct dc_hw_colorkey colorkey;
struct dc_hw_degamma degamma;
};
struct dc_hw_qos {
u8 low_value;
u8 high_value;
bool dirty;
};
struct dc_hw_read {
u32 reg;
u32 value;
};
struct dc_hw;
struct dc_hw_funcs {
void (*gamma)(struct dc_hw *hw);
void (*plane)(struct dc_hw *hw);
void (*display)(struct dc_hw *hw, struct dc_hw_display *display);
};
struct dc_hw {
enum dc_chip_rev rev;
enum dc_hw_out out[DC_DISPLAY_NUM];
void *hi_base;
void *reg_base;
#ifdef CONFIG_VERISILICON_MMU
void *mmu_base;
#endif
struct dc_hw_display display[DC_DISPLAY_NUM];
struct dc_hw_gamma gamma[DC_DISPLAY_NUM];
struct dc_hw_plane plane[DC_LAYER_NUM];
struct dc_hw_cursor cursor[DC_CURSOR_NUM];
struct dc_hw_qos qos;
struct dc_hw_funcs *func;
struct vs_dc_info *info;
};
int dc_hw_init(struct dc_hw *hw);
void dc_hw_deinit(struct dc_hw *hw);
void dc_hw_update_plane(struct dc_hw *hw, u8 id,
struct dc_hw_fb *fb, struct dc_hw_scale *scale,
struct dc_hw_position *pos, struct dc_hw_blend *blend);
void dc_hw_update_degamma(struct dc_hw *hw, u8 id, u32 mode);
void dc_hw_update_roi(struct dc_hw *hw, u8 id, struct dc_hw_roi *roi);
void dc_hw_update_colorkey(struct dc_hw *hw, u8 id,
struct dc_hw_colorkey *colorkey);
void dc_hw_update_qos(struct dc_hw *hw, struct dc_hw_qos *qos);
void dc_hw_update_cursor(struct dc_hw *hw, u8 id, struct dc_hw_cursor *cursor);
void dc_hw_update_gamma(struct dc_hw *hw, u8 id, u16 index,
u16 r, u16 g, u16 b);
void dc_hw_enable_gamma(struct dc_hw *hw, u8 id, bool enable);
void dc_hw_enable_dump(struct dc_hw *hw, u32 addr, u32 pitch);
void dc_hw_disable_dump(struct dc_hw *hw);
void dc_hw_setup_display(struct dc_hw *hw, struct dc_hw_display *display);
void dc_hw_enable_interrupt(struct dc_hw *hw, bool enable, u32 ctrc_mask);
u32 dc_hw_get_interrupt(struct dc_hw *hw);
bool dc_hw_check_underflow(struct dc_hw *hw);
void dc_hw_enable_shadow_register(struct dc_hw *hw, bool enable);
void dc_hw_set_out(struct dc_hw *hw, enum dc_hw_out out, u8 id);
void dc_hw_commit(struct dc_hw *hw);
#ifdef CONFIG_VERISILICON_DEC
void dc_hw_dec_init(struct dc_hw *hw);
void dc_hw_dec_stream_set(struct dc_hw *hw, u32 main_base_addr,
u32 ts_base_addr, u8 tile_mode, u8 align_mode,
u8 format, u8 depth, u8 stream_id);
void dc_hw_dec_stream_disable(struct dc_hw *hw, u8 stream_id);
#endif
#ifdef CONFIG_VERISILICON_MMU
int dc_hw_mmu_init(struct dc_hw *hw, dc_mmu_pt mmu);
void dc_hw_enable_mmu_prefetch(struct dc_hw *hw, bool enable);
#endif
#endif /* __VS_DC_HW_H__ */

View file

@ -0,0 +1,710 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/pagemap.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>
#include <asm/io.h>
#include <asm/delay.h>
#include "vs_dc_mmu.h"
static bool mmu_construct;
int _allocate_memory(u32 bytes, void **memory)
{
void *mem = NULL;
if (bytes == 0 || memory == NULL) {
pr_err("%s has invalid arguments.\n", __func__);
return -EINVAL;
}
if (bytes > PAGE_SIZE)
mem = vmalloc(bytes);
else
mem = kmalloc(bytes, GFP_KERNEL);
if (!mem) {
pr_err("%s out of memory.\n", __func__);
return -ENOMEM;
}
memset((u8 *)mem, 0, bytes);
*memory = mem;
return 0;
}
static int _create_mutex(void **mutex)
{
int ret = 0;
if (mutex == NULL)
return -EINVAL;
ret = _allocate_memory(sizeof(struct mutex), mutex);
if (ret)
return ret;
mutex_init(*(struct mutex **)mutex);
return 0;
}
static int _acquire_mutex(void *mutex, u32 timeout)
{
if (mutex == NULL) {
pr_err("%s has invalid argument.\n", __func__);
return -EINVAL;
}
if (timeout == DC_INFINITE) {
mutex_lock(mutex);
return 0;
}
for (;;) {
/* Try to acquire the mutex. */
if (mutex_trylock(mutex)) {
/* Success. */
return 0;
}
if (timeout-- == 0)
break;
/* Wait for 1 millisecond. */
udelay(1000);
}
return -ETIMEDOUT;
}
static int _release_mutex(void *mutex)
{
if (mutex == NULL) {
pr_err("%s has invalid argument.\n", __func__);
return -EINVAL;
}
mutex_unlock(mutex);
return 0;
}
static u32 _mtlb_offset(u32 address)
{
return (address & MMU_MTLB_MASK) >> MMU_MTLB_SHIFT;
}
static u32 _stlb_offset(u32 address)
{
return (address & MMU_STLB_4K_MASK) >> MMU_STLB_4K_SHIFT;
}
static u32 _address_to_index(dc_mmu_pt mmu, u32 address)
{
return _mtlb_offset(address) * MMU_STLB_4K_ENTRY_NUM + _stlb_offset(address);
}
static u32 _set_page(u32 page_address, u32 page_address_ext, bool writable)
{
u32 entry = page_address
/* AddressExt */
| (page_address_ext << 4)
/* Ignore exception */
| (0 << 1)
/* Present */
| (1 << 0);
if (writable) {
/* writable */
entry |= (1 << 2);
}
return entry;
}
static void _write_page_entry(u32 *page_entry, u32 entry_value)
{
*page_entry = entry_value;
}
static u32 _read_page_entry(u32 *page_entry)
{
return *page_entry;
}
int _allocate_stlb(dc_mmu_stlb_pt *stlb)
{
dc_mmu_stlb_pt stlb_t = NULL;
void *mem = NULL;
mem = kzalloc(sizeof(dc_mmu_stlb), GFP_KERNEL);
if (!mem)
return -ENOMEM;
stlb_t = (dc_mmu_stlb_pt)mem;
stlb_t->size = MMU_STLB_4K_SIZE;
*stlb = stlb_t;
return 0;
}
int _allocate_all_stlb(struct device *dev, dc_mmu_stlb_pt *stlb)
{
dc_mmu_stlb_pt stlb_t = NULL;
void *mem = NULL;
void *cookie = NULL;
dma_addr_t dma_addr;
size_t size;
mem = kzalloc(sizeof(dc_mmu_stlb), GFP_KERNEL);
if (!mem)
return -ENOMEM;
stlb_t = (dc_mmu_stlb_pt)mem;
stlb_t->size = MMU_STLB_4K_SIZE * MMU_MTLB_ENTRY_NUM;
size = PAGE_ALIGN(stlb_t->size);
cookie = dma_alloc_wc(dev, size, &dma_addr, GFP_KERNEL);
if (!cookie) {
dev_err(dev, "Failed to alloc stlb buffer.\n");
return -ENOMEM;
}
stlb_t->logical = cookie;
stlb_t->physBase = (u64)dma_addr;
memset(stlb_t->logical, 0, size);
*stlb = stlb_t;
return 0;
}
int _setup_process_address_space(struct device *dev, dc_mmu_pt mmu)
{
u32 *map = NULL;
u32 free, i;
u32 dynamic_mapping_entries, address;
dc_mmu_stlb_pt all_stlb;
int ret = 0;
dynamic_mapping_entries = MMU_MTLB_ENTRY_NUM;
mmu->dynamic_mapping_start = 0;
mmu->page_table_size = dynamic_mapping_entries * MMU_STLB_4K_SIZE;
mmu->page_table_entries = mmu->page_table_size / sizeof(u32);
ret = _allocate_memory(mmu->page_table_size,
(void **)&mmu->map_logical);
if (ret) {
pr_err("Failed to alloc mmu map buffer.\n");
return ret;
}
map = mmu->map_logical;
/* Initialize free area*/
free = mmu->page_table_entries;
_write_page_entry(map, (free << 8) | DC_MMU_FREE);
_write_page_entry(map + 1, ~0U);
mmu->heap_list = 0;
mmu->free_nodes = false;
ret = _allocate_all_stlb(dev, &all_stlb);
if (ret)
return ret;
for (i = 0; i < dynamic_mapping_entries; i++) {
dc_mmu_stlb_pt stlb;
dc_mmu_stlb_pt *stlbs = (dc_mmu_stlb_pt *)mmu->stlbs;
ret = _allocate_stlb(&stlb);
if (ret)
return ret;
stlb->physBase = all_stlb->physBase + i * MMU_STLB_4K_SIZE;
stlb->logical = all_stlb->logical + i * MMU_STLB_4K_SIZE / sizeof(u32);
stlbs[i] = stlb;
}
address = (u32)all_stlb->physBase;
ret = _acquire_mutex(mmu->page_table_mutex, DC_INFINITE);
if (ret)
return ret;
for (i = mmu->dynamic_mapping_start;
i < mmu->dynamic_mapping_start + dynamic_mapping_entries;
i++) {
u32 mtlb_entry;
mtlb_entry = address
| MMU_MTLB_4K_PAGE
| MMU_MTLB_PRESENT;
address += MMU_STLB_4K_SIZE;
/* Insert Slave TLB address to Master TLB entry.*/
_write_page_entry(mmu->mtlb_logical + i, mtlb_entry);
}
_release_mutex(mmu->page_table_mutex);
return 0;
}
/* MMU Construct */
int dc_mmu_construct(struct device *dev, dc_mmu_pt *mmu)
{
dc_mmu_pt mmu_t = NULL;
void *mem = NULL;
void *cookie = NULL, *cookie_safe = NULL;
dma_addr_t dma_addr, dma_addr_safe;
u32 size = 0;
int ret = 0;
if (mmu_construct)
return 0;
mem = kzalloc(sizeof(dc_mmu), GFP_KERNEL);
if (!mem)
return -ENOMEM;
mmu_t = (dc_mmu_pt)mem;
mmu_t->mtlb_bytes = MMU_MTLB_SIZE;
size = PAGE_ALIGN(mmu_t->mtlb_bytes);
/* Allocate MTLB */
cookie = dma_alloc_wc(dev, size, &dma_addr, GFP_KERNEL);
if (!cookie) {
dev_err(dev, "Failed to alloc mtlb buffer.\n");
return -ENOMEM;
}
mmu_t->mtlb_logical = cookie;
mmu_t->mtlb_physical = (u64)dma_addr;
memset(mmu_t->mtlb_logical, 0, size);
size = MMU_MTLB_ENTRY_NUM * sizeof(dc_mmu_stlb_pt);
ret = _allocate_memory(size, &mmu_t->stlbs);
if (ret)
return ret;
ret = _create_mutex(&mmu_t->page_table_mutex);
if (ret)
return ret;
mmu_t->mode = MMU_MODE_1K;
ret = _setup_process_address_space(dev, mmu_t);
if (ret)
return ret;
/* Allocate safe page */
cookie_safe = dma_alloc_wc(dev, 4096, &dma_addr_safe, GFP_KERNEL);
if (!cookie_safe) {
dev_err(dev, "Failed to alloc safe page.\n");
return -ENOMEM;
}
mmu_t->safe_page_logical = cookie_safe;
mmu_t->safe_page_physical = (u64)dma_addr_safe;
memset(mmu_t->safe_page_logical, 0, size);
*mmu = mmu_t;
mmu_construct = true;
return 0;
}
int dc_mmu_get_page_entry(dc_mmu_pt mmu, u32 address, u32 **page_table)
{
dc_mmu_stlb_pt stlb;
dc_mmu_stlb_pt *stlbs = (dc_mmu_stlb_pt *)mmu->stlbs;
u32 mtlb_offset = _mtlb_offset(address);
u32 stlb_offset = _stlb_offset(address);
stlb = stlbs[mtlb_offset - mmu->dynamic_mapping_start];
if (stlb == NULL) {
pr_err("BUG: invalid stlb, mmu=%p stlbs=%p mtlb_offset=0x%x %s(%d)\n",
mmu, stlbs, mtlb_offset, __func__, __LINE__);
return -ENXIO;
}
*page_table = &stlb->logical[stlb_offset];
return 0;
}
int _link(dc_mmu_pt mmu, u32 index, u32 node)
{
if (index >= mmu->page_table_entries) {
mmu->heap_list = node;
} else {
u32 *map = mmu->map_logical;
switch (DC_ENTRY_TYPE(_read_page_entry(&map[index]))) {
case DC_MMU_SINGLE:
/* Previous is a single node, link to it*/
_write_page_entry(&map[index], (node << 8) | DC_MMU_SINGLE);
break;
case DC_MMU_FREE:
/* Link to FREE TYPE node */
_write_page_entry(&map[index + 1], node);
break;
default:
pr_err("MMU table corrupted at index %u!", index);
return -EINVAL;
}
}
return 0;
}
int _add_free(dc_mmu_pt mmu, u32 index, u32 node, u32 count)
{
u32 *map = mmu->map_logical;
if (count == 1) {
/* Initialize a single page node */
_write_page_entry(map + node, DC_SINGLE_PAGE_NODE_INITIALIZE | DC_MMU_SINGLE);
} else {
/* Initialize the FREE node*/
_write_page_entry(map + node, (count << 8) | DC_MMU_FREE);
_write_page_entry(map + node + 1, ~0U);
}
return _link(mmu, index, node);
}
/* Collect free nodes */
int _collect(dc_mmu_pt mmu)
{
u32 *map = mmu->map_logical;
u32 count = 0, start = 0, i = 0;
u32 previous = ~0U;
int ret = 0;
mmu->heap_list = ~0U;
mmu->free_nodes = false;
/* Walk the entire page table */
for (i = 0; i < mmu->page_table_entries; i++) {
switch (DC_ENTRY_TYPE(_read_page_entry(&map[i]))) {
case DC_MMU_SINGLE:
if (count++ == 0) {
/* Set new start node */
start = i;
}
break;
case DC_MMU_FREE:
if (count == 0) {
/* Set new start node */
start = i;
}
count += _read_page_entry(&map[i]) >> 8;
/* Advance the index of the page table */
i += (_read_page_entry(&map[i]) >> 8) - 1;
break;
case DC_MMU_USED:
/* Meet used node, start to collect */
if (count > 0) {
/* Add free node to list*/
ret = _add_free(mmu, previous, start, count);
if (ret)
return ret;
/* Reset previous unused node index */
previous = start;
count = 0;
}
break;
default:
pr_err("MMU page table corrupted at index %u!", i);
return -EINVAL;
}
}
/* If left node is an open node. */
if (count > 0) {
ret = _add_free(mmu, previous, start, count);
if (ret)
return ret;
}
return 0;
}
int _fill_page_table(u32 *page_table, u32 page_count, u32 entry_value)
{
u32 i;
for (i = 0; i < page_count; i++)
_write_page_entry(page_table + i, entry_value);
return 0;
}
int dc_mmu_allocate_pages(dc_mmu_pt mmu, u32 page_count, u32 *address)
{
bool got = false, acquired = false;
u32 *map;
u32 index = 0, vaddr, left;
u32 previous = ~0U;
u32 mtlb_offset, stlb_offset;
int ret = 0;
if (page_count == 0 || page_count > mmu->page_table_entries) {
pr_err("%s has invalid arguments.\n", __func__);
return -EINVAL;
}
_acquire_mutex(mmu->page_table_mutex, DC_INFINITE);
acquired = true;
for (map = mmu->map_logical; !got;) {
for (index = mmu->heap_list; !got && (index < mmu->page_table_entries);) {
switch (DC_ENTRY_TYPE(_read_page_entry(&map[index]))) {
case DC_MMU_SINGLE:
if (page_count == 1) {
got = true;
} else {
/* Move to next node */
previous = index;
index = _read_page_entry(&map[index]) >> 8;
}
break;
case DC_MMU_FREE:
if (page_count <= (_read_page_entry(&map[index]) >> 8)) {
got = true;
} else {
/* Move to next node */
previous = index;
index = _read_page_entry(&map[index + 1]);
}
break;
default:
/* Only link SINGLE and FREE node */
pr_err("MMU table corrupted at index %u!", index);
ret = -EINVAL;
goto OnError;
}
}
/* If out of index */
if (index >= mmu->page_table_entries) {
if (mmu->free_nodes) {
/* Collect the free node */
ret = _collect(mmu);
if (ret)
goto OnError;
} else {
ret = -ENODATA;
goto OnError;
}
}
}
switch (DC_ENTRY_TYPE(_read_page_entry(&map[index]))) {
case DC_MMU_SINGLE:
/* Unlink single node from node list */
ret = _link(mmu, previous, _read_page_entry(&map[index]) >> 8);
if (ret)
goto OnError;
break;
case DC_MMU_FREE:
left = (_read_page_entry(&map[index]) >> 8) - page_count;
switch (left) {
case 0:
/* Unlink the entire FREE type node */
ret = _link(mmu, previous, _read_page_entry(&map[index + 1]));
if (ret)
goto OnError;
break;
case 1:
/* Keep the map[index] as a single node,
* mark the left as used
*/
_write_page_entry(&map[index],
(_read_page_entry(&map[index + 1]) << 8) |
DC_MMU_SINGLE);
index++;
break;
default:
/* FREE type node left */
_write_page_entry(&map[index],
(left << 8) | DC_MMU_FREE);
index += left;
break;
}
break;
default:
/* Only link SINGLE and FREE node */
pr_err("MMU table corrupted at index %u!", index);
ret = -EINVAL;
goto OnError;
}
/* Mark node as used */
ret = _fill_page_table(&map[index], page_count, DC_MMU_USED);
if (ret)
goto OnError;
_release_mutex(mmu->page_table_mutex);
mtlb_offset = index / MMU_STLB_4K_ENTRY_NUM + mmu->dynamic_mapping_start;
stlb_offset = index % MMU_STLB_4K_ENTRY_NUM;
vaddr = (mtlb_offset << MMU_MTLB_SHIFT) | (stlb_offset << MMU_STLB_4K_SHIFT);
if (address != NULL)
*address = vaddr;
return 0;
OnError:
if (acquired)
_release_mutex(mmu->page_table_mutex);
return ret;
}
int dc_mmu_free_pages(dc_mmu_pt mmu, u32 address, u32 page_count)
{
u32 *node;
if (page_count == 0)
return -EINVAL;
node = mmu->map_logical + _address_to_index(mmu, address);
_acquire_mutex(mmu->page_table_mutex, DC_INFINITE);
if (page_count == 1) {
/* Mark the Single page node free */
_write_page_entry(node, DC_SINGLE_PAGE_NODE_INITIALIZE | DC_MMU_SINGLE);
} else {
/* Mark the FREE type node free */
_write_page_entry(node, (page_count << 8) | DC_MMU_FREE);
_write_page_entry(node + 1, ~0U);
}
mmu->free_nodes = true;
_release_mutex(mmu->page_table_mutex);
return 0;
}
int dc_mmu_set_page(dc_mmu_pt mmu, u64 page_address, u32 *page_entry)
{
u32 address_ext;
u32 address;
if (page_entry == NULL || (page_address & 0xFFF))
return -EINVAL;
/* [31:0]. */
address = (u32)(page_address & 0xFFFFFFFF);
/* [39:32]. */
address_ext = (u32)((page_address >> 32) & 0xFF);
_write_page_entry(page_entry, _set_page(address, address_ext, true));
return 0;
}
int dc_mmu_map_memory(dc_mmu_pt mmu, u64 physical, u32 page_count,
u32 *address, bool continuous, bool security)
{
u32 virutal_address, i = 0;
u32 mtlb_num, mtlb_entry, mtlb_offset;
bool allocated = false;
int ret = 0;
ret = dc_mmu_allocate_pages(mmu, page_count, &virutal_address);
if (ret)
goto OnError;
*address = virutal_address;
allocated = true;
/*Fill mtlb security bit*/
mtlb_num = _mtlb_offset(virutal_address + page_count * MMU_PAGE_4K_SIZE - 1) -
_mtlb_offset(virutal_address) + 1;
mtlb_offset = _mtlb_offset(virutal_address);
mtlb_entry = mmu->mtlb_logical[mtlb_offset];
for (i = 0; i < mtlb_num ; i++) {
mtlb_entry = mmu->mtlb_logical[mtlb_offset + i];
if (security) {
mtlb_entry = mtlb_entry
| MMU_MTLB_SECURITY
| MMU_MTLB_EXCEPTION;
_write_page_entry(&mmu->mtlb_logical[mtlb_offset + i], mtlb_entry);
} else {
mtlb_entry = mtlb_entry & (~MMU_MTLB_SECURITY);
_write_page_entry(&mmu->mtlb_logical[mtlb_offset + i], mtlb_entry);
}
}
/* Fill in page table */
for (i = 0; i < page_count; i++) {
u64 page_phy;
u32 *page_entry;
struct page **pages;
if (continuous == true) {
page_phy = physical + i * MMU_PAGE_4K_SIZE;
} else {
pages = (struct page **)physical;
page_phy = page_to_phys(pages[i]);
}
ret = dc_mmu_get_page_entry(mmu, virutal_address, &page_entry);
if (ret)
goto OnError;
/* Write the page address to the page entry */
ret = dc_mmu_set_page(mmu, page_phy, page_entry);
if (ret)
goto OnError;
/* Get next page */
virutal_address += MMU_PAGE_4K_SIZE;
}
return 0;
OnError:
if (allocated)
dc_mmu_free_pages(mmu, virutal_address, page_count);
pr_info("%s fail!\n", __func__);
return ret;
}
int dc_mmu_unmap_memory(dc_mmu_pt mmu, u32 gpu_address, u32 page_count)
{
return dc_mmu_free_pages(mmu, gpu_address, page_count);
}

View file

@ -0,0 +1,98 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#ifndef _VS_DC_MMU_H_
#define _VS_DC_MMU_H_
#include "vs_type.h"
#define DC_INFINITE ((u32)(~0U))
#define DC_ENTRY_TYPE(x) (x & 0xF0)
#define DC_SINGLE_PAGE_NODE_INITIALIZE (~((1U << 8) - 1))
#define DC_INVALID_PHYSICAL_ADDRESS ~0ULL
#define DC_INVALID_ADDRESS ~0U
/* 1k mode */
#define MMU_MTLB_SHIFT 24
#define MMU_STLB_4K_SHIFT 12
#define MMU_MTLB_BITS (32 - MMU_MTLB_SHIFT)
#define MMU_PAGE_4K_BITS MMU_STLB_4K_SHIFT
#define MMU_STLB_4K_BITS (32 - MMU_MTLB_BITS - MMU_PAGE_4K_BITS)
#define MMU_MTLB_ENTRY_NUM (1 << MMU_MTLB_BITS)
#define MMU_MTLB_SIZE (MMU_MTLB_ENTRY_NUM << 2)
#define MMU_STLB_4K_ENTRY_NUM (1 << MMU_STLB_4K_BITS)
#define MMU_STLB_4K_SIZE (MMU_STLB_4K_ENTRY_NUM << 2)
#define MMU_PAGE_4K_SIZE (1 << MMU_STLB_4K_SHIFT)
#define MMU_MTLB_MASK (~((1U << MMU_MTLB_SHIFT)-1))
#define MMU_STLB_4K_MASK ((~0U << MMU_STLB_4K_SHIFT) ^ MMU_MTLB_MASK)
#define MMU_PAGE_4K_MASK (MMU_PAGE_4K_SIZE - 1)
/* page offset definitions. */
#define MMU_OFFSET_4K_BITS (32 - MMU_MTLB_BITS - MMU_STLB_4K_BITS)
#define MMU_OFFSET_4K_MASK ((1U << MMU_OFFSET_4K_BITS) - 1)
#define MMU_MTLB_PRESENT 0x00000001
#define MMU_MTLB_EXCEPTION 0x00000002
#define MMU_MTLB_SECURITY 0x00000010
#define MMU_MTLB_4K_PAGE 0x00000000
typedef enum _dc_mmu_type {
DC_MMU_USED = (0 << 4),
DC_MMU_SINGLE = (1 << 4),
DC_MMU_FREE = (2 << 4),
} dc_mmu_type;
typedef enum _dc_mmu_mode {
MMU_MODE_1K,
MMU_MODE_4K,
} dc_mmu_mode;
typedef struct _dc_mmu_stlb {
u32 *logical;
void *physical;
u32 size;
u64 physBase;
u32 pageCount;
} dc_mmu_stlb, *dc_mmu_stlb_pt;
typedef struct _dc_mmu {
u32 mtlb_bytes;
u64 mtlb_physical;
u32 *mtlb_logical;
void *safe_page_logical;
u64 safe_page_physical;
u32 dynamic_mapping_start;
void *stlbs;
u64 stlb_physicals[MMU_MTLB_ENTRY_NUM];
u32 page_table_entries;
u32 page_table_size;
u32 heap_list;
u32 *map_logical;
bool free_nodes;
void *page_table_mutex;
dc_mmu_mode mode;
void *static_stlb;
} dc_mmu, *dc_mmu_pt;
int dc_mmu_construct(struct device *dev, dc_mmu_pt *mmu);
int dc_mmu_map_memory(dc_mmu_pt mmu, u64 physical, u32 page_count,
u32 *address, bool continuous, bool security);
int dc_mmu_unmap_memory(dc_mmu_pt mmu, u32 gpu_address, u32 page_count);
#endif /* _VS_DC_MMU_H_ */

View file

@ -0,0 +1,483 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#include <linux/of_graph.h>
#include <linux/component.h>
#include <linux/iommu.h>
#include <linux/version.h>
#include <linux/delay.h>
#include <drm/drm_of.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_modeset_helper.h>
#if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
#include <drm/drm_prime.h>
#include <drm/drm_vblank.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_debugfs.h>
#endif
#include <linux/of_reserved_mem.h>
#include <drm/drm_aperture.h>
#include "vs_drv.h"
#include "vs_fb.h"
#include "vs_gem.h"
#include "vs_plane.h"
#include "vs_crtc.h"
#include "vs_simple_enc.h"
#include "vs_dc.h"
#include "vs_virtual.h"
#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/reset.h>
extern struct platform_driver starfive_encoder_driver;
#define DRV_NAME "starfive"
#define DRV_DESC "VeriSilicon DRM driver"
#define DRV_DATE "20191101"
#define DRV_MAJOR 1
#define DRV_MINOR 0
static bool has_iommu = true;
static struct platform_driver vs_drm_platform_driver;
static const struct file_operations fops = {
.owner = THIS_MODULE,
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
.compat_ioctl = drm_compat_ioctl,
.poll = drm_poll,
.read = drm_read,
.llseek = noop_llseek,
.mmap = drm_gem_mmap,
};
#ifdef CONFIG_DEBUG_FS
static int vs_debugfs_planes_show(struct seq_file *s, void *data)
{
struct drm_info_node *node = (struct drm_info_node *)s->private;
struct drm_device *dev = node->minor->dev;
struct drm_plane *plane;
list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
struct drm_plane_state *state = plane->state;
struct vs_plane_state *plane_state = to_vs_plane_state(state);
seq_printf(s, "plane[%u]: %s\n", plane->base.id, plane->name);
seq_printf(s, "\tcrtc = %s\n", state->crtc ?
state->crtc->name : "(null)");
seq_printf(s, "\tcrtc id = %u\n", state->crtc ?
state->crtc->base.id : 0);
seq_printf(s, "\tcrtc-pos = " DRM_RECT_FMT "\n",
DRM_RECT_ARG(&plane_state->status.dest));
seq_printf(s, "\tsrc-pos = " DRM_RECT_FP_FMT "\n",
DRM_RECT_FP_ARG(&plane_state->status.src));
//seq_printf(s, "\tformat = %s\n", state->fb ?
// plane_state->status.format_name.str : "(null)");
seq_printf(s, "\trotation = 0x%x\n", state->rotation);
seq_printf(s, "\ttiling = %u\n",
plane_state->status.tile_mode);
seq_puts(s, "\n");
}
return 0;
}
static struct drm_info_list vs_debugfs_list[] = {
{ "planes", vs_debugfs_planes_show, 0, NULL },
};
#if KERNEL_VERSION(5, 8, 0) <= LINUX_VERSION_CODE
static void vs_debugfs_init(struct drm_minor *minor)
{
drm_debugfs_create_files(vs_debugfs_list,
ARRAY_SIZE(vs_debugfs_list),
minor->debugfs_root, minor);
}
#else
static int vs_debugfs_init(struct drm_minor *minor)
{
struct drm_device *dev = minor->dev;
int ret;
ret = drm_debugfs_create_files(vs_debugfs_list,
ARRAY_SIZE(vs_debugfs_list),
minor->debugfs_root, minor);
if (ret)
dev_err(dev->dev, "could not install vs_debugfs_list.\n");
return ret;
}
#endif
#endif
static struct drm_driver vs_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
.lastclose = drm_fb_helper_lastclose,
.gem_prime_import = vs_gem_prime_import,
.gem_prime_import_sg_table = vs_gem_prime_import_sg_table,
.dumb_create = vs_gem_dumb_create,
#ifdef CONFIG_DEBUG_FS
.debugfs_init = vs_debugfs_init,
#endif
.fops = &fops,
.name = DRV_NAME,
.desc = DRV_DESC,
.date = DRV_DATE,
.major = DRV_MAJOR,
.minor = DRV_MINOR,
};
int vs_drm_iommu_attach_device(struct drm_device *drm_dev,
struct device *dev)
{
struct vs_drm_private *priv = drm_dev->dev_private;
int ret;
if (!has_iommu)
return 0;
if (!priv->domain) {
priv->domain = iommu_get_domain_for_dev(dev);
if (IS_ERR(priv->domain))
return PTR_ERR(priv->domain);
priv->dma_dev = dev;
}
ret = iommu_attach_device(priv->domain, dev);
if (ret) {
DRM_DEV_ERROR(dev, "Failed to attach iommu device\n");
return ret;
}
return 0;
}
void vs_drm_iommu_detach_device(struct drm_device *drm_dev,
struct device *dev)
{
struct vs_drm_private *priv = drm_dev->dev_private;
if (!has_iommu)
return;
iommu_detach_device(priv->domain, dev);
if (priv->dma_dev == dev)
priv->dma_dev = drm_dev->dev;
}
void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
unsigned int alignment)
{
struct vs_drm_private *priv = drm_dev->dev_private;
if (alignment > priv->pitch_alignment)
priv->pitch_alignment = alignment;
}
static int vs_drm_bind(struct device *dev)
{
struct drm_device *drm_dev;
struct vs_drm_private *priv;
int ret;
#if KERNEL_VERSION(2, 6, 24) <= LINUX_VERSION_CODE
static u64 dma_mask = DMA_BIT_MASK(40);
#else
static u64 dma_mask = DMA_40BIT_MASK;
#endif
/* Remove existing drivers that may own the framebuffer memory. */
ret = drm_aperture_remove_framebuffers(&vs_drm_driver);
if (ret) {
DRM_DEV_ERROR(dev,
"Failed to remove existing framebuffers - %d.\n",
ret);
return ret;
}
drm_dev = drm_dev_alloc(&vs_drm_driver, dev);
if (IS_ERR(drm_dev))
return PTR_ERR(drm_dev);
dev_set_drvdata(dev, drm_dev);
priv = devm_kzalloc(drm_dev->dev, sizeof(struct vs_drm_private),
GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
goto err_put_dev;
}
priv->pitch_alignment = 32;
priv->dma_dev = drm_dev->dev;
priv->dma_dev->coherent_dma_mask = dma_mask;
drm_dev->dev_private = priv;
drm_mode_config_init(drm_dev);
/* Now try and bind all our sub-components */
ret = component_bind_all(dev, drm_dev);
if (ret)
goto err_mode;
vs_mode_config_init(drm_dev);
ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
if (ret)
goto err_bind;
drm_mode_config_reset(drm_dev);
drm_dev->irq_enabled = true;
drm_kms_helper_poll_init(drm_dev);
ret = drm_dev_register(drm_dev, 0);
if (ret)
goto err_helper;
drm_fbdev_generic_setup(drm_dev, 32);
return 0;
err_helper:
drm_kms_helper_poll_fini(drm_dev);
err_bind:
component_unbind_all(drm_dev->dev, drm_dev);
err_mode:
drm_mode_config_cleanup(drm_dev);
if (priv->domain)
iommu_domain_free(priv->domain);
err_put_dev:
drm_dev->dev_private = NULL;
dev_set_drvdata(dev, NULL);
drm_dev_put(drm_dev);
return ret;
}
static void vs_drm_unbind(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
struct vs_drm_private *priv = drm_dev->dev_private;
drm_dev_unregister(drm_dev);
drm_kms_helper_poll_fini(drm_dev);
component_unbind_all(drm_dev->dev, drm_dev);
drm_mode_config_cleanup(drm_dev);
if (priv->domain) {
iommu_domain_free(priv->domain);
priv->domain = NULL;
}
drm_dev->dev_private = NULL;
dev_set_drvdata(dev, NULL);
drm_dev_put(drm_dev);
}
static const struct component_master_ops vs_drm_ops = {
.bind = vs_drm_bind,
.unbind = vs_drm_unbind,
};
static struct platform_driver *drm_sub_drivers[] = {
/* put display control driver at start */
&dc_platform_driver,
/* connector */
#ifdef CONFIG_STARFIVE_INNO_HDMI
&inno_hdmi_driver,
#endif
&simple_encoder_driver,
#ifdef CONFIG_VERISILICON_VIRTUAL_DISPLAY
&virtual_display_platform_driver,
#endif
};
#define NUM_DRM_DRIVERS \
(sizeof(drm_sub_drivers) / sizeof(struct platform_driver *))
static int compare_dev(struct device *dev, void *data)
{
return dev == (struct device *)data;
}
static struct component_match *vs_drm_match_add(struct device *dev)
{
struct component_match *match = NULL;
int i;
for (i = 0; i < NUM_DRM_DRIVERS; ++i) {
struct platform_driver *drv = drm_sub_drivers[i];
struct device *p = NULL, *d;
while ((d = platform_find_device_by_driver(p, &drv->driver))) {
put_device(p);
component_match_add(dev, &match, compare_dev, d);
p = d;
}
put_device(p);
}
return match ?: ERR_PTR(-ENODEV);
}
static int vs_drm_platform_of_probe(struct device *dev)
{
struct device_node *np = dev->of_node;
struct device_node *port;
bool found = false;
int i;
if (!np)
return -ENODEV;
for (i = 0;; i++) {
struct device_node *iommu;
port = of_parse_phandle(np, "ports", i);
if (!port)
break;
if (!of_device_is_available(port->parent)) {
of_node_put(port);
continue;
}
iommu = of_parse_phandle(port->parent, "iommus", 0);
/*
* if there is a crtc not support iommu, force set all
* crtc use non-iommu buffer.
*/
if (!iommu || !of_device_is_available(iommu->parent))
has_iommu = false;
found = true;
of_node_put(iommu);
of_node_put(port);
}
if (i == 0) {
DRM_DEV_ERROR(dev, "missing 'ports' property\n");
return -ENODEV;
}
if (!found) {
DRM_DEV_ERROR(dev, "No available DC found.\n");
return -ENODEV;
}
return 0;
}
static int vs_drm_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct component_match *match;
int ret;
ret = vs_drm_platform_of_probe(dev);
if (ret)
return ret;
match = vs_drm_match_add(dev);
if (IS_ERR(match))
return PTR_ERR(match);
return component_master_add_with_match(dev, &vs_drm_ops, match);
}
static int vs_drm_platform_remove(struct platform_device *pdev)
{
component_master_del(&pdev->dev, &vs_drm_ops);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int vs_drm_suspend(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);
dev_info(dev, "vs_drm_suspend\n");
return drm_mode_config_helper_suspend(drm);
}
static int vs_drm_resume(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);
dev_info(dev, "vs_drm_resume\n");
return drm_mode_config_helper_resume(drm);
}
#endif
static SIMPLE_DEV_PM_OPS(vs_drm_pm_ops, vs_drm_suspend, vs_drm_resume);
static const struct of_device_id vs_drm_dt_ids[] = {
{ .compatible = "verisilicon,display-subsystem", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, vs_drm_dt_ids);
static struct platform_driver vs_drm_platform_driver = {
.probe = vs_drm_platform_probe,
.remove = vs_drm_platform_remove,
.driver = {
.name = DRV_NAME,
.of_match_table = vs_drm_dt_ids,
.pm = &vs_drm_pm_ops,
},
};
static int __init vs_drm_init(void)
{
int ret;
ret = platform_register_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
if (ret)
return ret;
ret = platform_driver_register(&vs_drm_platform_driver);
if (ret)
platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
return ret;
}
static void __exit vs_drm_fini(void)
{
platform_driver_unregister(&vs_drm_platform_driver);
platform_unregister_drivers(drm_sub_drivers, NUM_DRM_DRIVERS);
}
late_initcall_sync(vs_drm_init);
module_exit(vs_drm_fini);
MODULE_DESCRIPTION("VeriSilicon DRM Driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,72 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#ifndef __VS_DRV_H__
#define __VS_DRV_H__
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/version.h>
#include <drm/drm_device.h>
#include <drm/drm_gem.h>
#if KERNEL_VERSION(5, 5, 0) > LINUX_VERSION_CODE
#include <drm/drmP.h>
#endif
#include "vs_plane.h"
#ifdef CONFIG_VERISILICON_MMU
#include "vs_dc_mmu.h"
#endif
/*
*
* @dma_dev: device for DMA API.
* - use the first attached device if support iommu
else use drm device (only contiguous buffer support)
* @domain: iommu domain for DRM.
* - all DC IOMMU share same domain to reduce mapping
* @pitch_alignment: buffer pitch alignment required by sub-devices.
*
*/
struct vs_drm_private {
struct device *dma_dev;
struct iommu_domain *domain;
#ifdef CONFIG_VERISILICON_MMU
dc_mmu * mmu;
#endif
unsigned int pitch_alignment;
};
int vs_drm_iommu_attach_device(struct drm_device *drm_dev,
struct device *dev);
void vs_drm_iommu_detach_device(struct drm_device *drm_dev,
struct device *dev);
void vs_drm_update_pitch_alignment(struct drm_device *drm_dev,
unsigned int alignment);
static inline struct device *to_dma_dev(struct drm_device *dev)
{
struct vs_drm_private *priv = dev->dev_private;
return priv->dma_dev;
}
static inline bool is_iommu_enabled(struct drm_device *dev)
{
struct vs_drm_private *priv = dev->dev_private;
return priv->domain != NULL ? true : false;
}
#ifdef CONFIG_STARFIVE_INNO_HDMI
extern struct platform_driver inno_hdmi_driver;
#endif
#endif /* __VS_DRV_H__ */

View file

@ -0,0 +1,192 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#include <linux/module.h>
#include <linux/version.h>
#if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE
#include <drm/drm_fourcc.h>
#else
#include <drm/drmP.h>
#endif
#include <drm/drm_gem.h>
#include <drm/drm_crtc.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include "vs_fb.h"
#include "vs_gem.h"
#define fourcc_mod_vs_get_type(val) \
(((val) & DRM_FORMAT_MOD_VS_TYPE_MASK) >> 54)
static struct drm_framebuffer_funcs vs_fb_funcs = {
.create_handle = drm_gem_fb_create_handle,
.destroy = drm_gem_fb_destroy,
.dirty = drm_atomic_helper_dirtyfb,
};
static struct drm_framebuffer *
vs_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd,
struct vs_gem_object **obj, unsigned int num_planes)
{
struct drm_framebuffer *fb;
int ret, i;
fb = kzalloc(sizeof(*fb), GFP_KERNEL);
if (!fb)
return ERR_PTR(-ENOMEM);
drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
for (i = 0; i < num_planes; i++)
fb->obj[i] = &obj[i]->base;
ret = drm_framebuffer_init(dev, fb, &vs_fb_funcs);
if (ret) {
dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
ret);
kfree(fb);
return ERR_PTR(ret);
}
return fb;
}
static struct drm_framebuffer *vs_fb_create(struct drm_device *dev,
struct drm_file *file_priv,
const struct drm_mode_fb_cmd2 *mode_cmd)
{
struct drm_framebuffer *fb;
const struct drm_format_info *info;
struct vs_gem_object *objs[MAX_NUM_PLANES];
struct drm_gem_object *obj;
unsigned int height, size;
unsigned char i, num_planes;
int ret = 0;
info = drm_get_format_info(dev, mode_cmd);
if (!info)
return ERR_PTR(-EINVAL);
num_planes = info->num_planes;
if (num_planes > MAX_NUM_PLANES)
return ERR_PTR(-EINVAL);
for (i = 0; i < num_planes; i++) {
obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]);
if (!obj) {
dev_err(dev->dev, "Failed to lookup GEM object.\n");
ret = -ENXIO;
goto err;
}
height = drm_format_info_plane_height(info,
mode_cmd->height, i);
size = height * mode_cmd->pitches[i] + mode_cmd->offsets[i];
if (obj->size < size) {
#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE
drm_gem_object_put(obj);
#else
drm_gem_object_put_unlocked(obj);
#endif
ret = -EINVAL;
goto err;
}
objs[i] = to_vs_gem_object(obj);
}
fb = vs_fb_alloc(dev, mode_cmd, objs, i);
if (IS_ERR(fb)) {
ret = PTR_ERR(fb);
goto err;
}
return fb;
err:
for (; i > 0; i--)
#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE
drm_gem_object_put(&objs[i-1]->base);
#else
drm_gem_object_put_unlocked(&objs[i-1]->base);
#endif
return ERR_PTR(ret);
}
struct vs_gem_object *vs_fb_get_gem_obj(struct drm_framebuffer *fb,
unsigned char plane)
{
if (plane > MAX_NUM_PLANES)
return NULL;
return to_vs_gem_object(fb->obj[plane]);
}
static const struct drm_format_info vs_formats[] = {
{.format = DRM_FORMAT_NV12, .depth = 0, .num_planes = 2, .char_per_block = { 20, 40, 0 },
.block_w = { 4, 4, 0 }, .block_h = { 4, 4, 0 }, .hsub = 2, .vsub = 2, .is_yuv = true},
{.format = DRM_FORMAT_YUV444, .depth = 0, .num_planes = 3, .char_per_block = { 20, 20, 20 },
.block_w = { 4, 4, 4 }, .block_h = { 4, 4, 4 }, .hsub = 1, .vsub = 1, .is_yuv = true},
};
static const struct drm_format_info *
vs_lookup_format_info(const struct drm_format_info formats[],
int num_formats, u32 format)
{
int i;
for (i = 0; i < num_formats; i++) {
if (formats[i].format == format)
return &formats[i];
}
return NULL;
}
static const struct drm_format_info *
vs_get_format_info(const struct drm_mode_fb_cmd2 *cmd)
{
if (fourcc_mod_vs_get_type(cmd->modifier[0]) ==
DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT)
return vs_lookup_format_info(vs_formats, ARRAY_SIZE(vs_formats),
cmd->pixel_format);
else
return NULL;
}
static const struct drm_mode_config_funcs vs_mode_config_funcs = {
.fb_create = vs_fb_create,
.get_format_info = vs_get_format_info,
.output_poll_changed = drm_fb_helper_output_poll_changed,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
static struct drm_mode_config_helper_funcs vs_mode_config_helpers = {
.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
};
void vs_mode_config_init(struct drm_device *dev)
{
//dev->mode_config.allow_fb_modifiers = true;
if (dev->mode_config.max_width == 0 ||
dev->mode_config.max_height == 0) {
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
dev->mode_config.max_width = 4096;
dev->mode_config.max_height = 4096;
}
dev->mode_config.funcs = &vs_mode_config_funcs;
dev->mode_config.helper_private = &vs_mode_config_helpers;
}

View file

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#ifndef __VS_FB_H__
#define __VS_FB_H__
struct vs_gem_object *vs_fb_get_gem_obj(struct drm_framebuffer *fb,
unsigned char plane);
void vs_mode_config_init(struct drm_device *dev);
#endif /* __VS_FB_H__ */

View file

@ -0,0 +1,552 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#include <linux/dma-buf.h>
#include <linux/of_reserved_mem.h>
#include <drm/drm_device.h>
#include <drm/drm_gem_dma_helper.h>
#include "vs_drv.h"
#include "vs_gem.h"
static const struct drm_gem_object_funcs vs_gem_default_funcs;
static void nonseq_free(struct page **pages, unsigned int nr_page)
{
u32 i;
if (!pages)
return;
for (i = 0; i < nr_page; i++)
__free_page(pages[i]);
}
#ifdef CONFIG_VERISILICON_MMU
static int get_pages(unsigned int nr_page, struct vs_gem_object *vs_obj)
{
struct page *pages;
u32 i, num_page, page_count = 0;
int order = 0;
gfp_t gfp = GFP_KERNEL;
if (!vs_obj->pages)
return -EINVAL;
gfp &= ~__GFP_HIGHMEM;
gfp |= __GFP_DMA32;
num_page = nr_page;
do {
pages = NULL;
order = get_order(num_page * PAGE_SIZE);
num_page = 1 << order;
if ((num_page + page_count > nr_page) || (order >= MAX_ORDER)) {
num_page = num_page >> 1;
continue;
}
pages = alloc_pages(gfp, order);
if (!pages) {
if (num_page == 1) {
nonseq_free(vs_obj->pages, page_count);
return -ENOMEM;
}
num_page = num_page >> 1;
} else {
for (i = 0; i < num_page; i++) {
vs_obj->pages[page_count + i] = &pages[i];
SetPageReserved(vs_obj->pages[page_count + i]);
}
page_count += num_page;
num_page = nr_page - page_count;
}
} while (page_count < nr_page);
vs_obj->get_pages = true;
return 0;
}
#endif
static void put_pages(unsigned int nr_page, struct vs_gem_object *vs_obj)
{
u32 i;
for (i = 0; i < nr_page; i++)
ClearPageReserved(vs_obj->pages[i]);
nonseq_free(vs_obj->pages, nr_page);
}
static int vs_gem_alloc_buf(struct vs_gem_object *vs_obj)
{
struct drm_device *dev = vs_obj->base.dev;
unsigned int nr_pages;
struct sg_table sgt;
int ret = -ENOMEM;
#ifdef CONFIG_VERISILICON_MMU
struct vs_drm_private *priv = dev->dev_private;
#endif
if (vs_obj->dma_addr) {
DRM_DEV_DEBUG_KMS(dev->dev, "already allocated.\n");
return 0;
}
vs_obj->dma_attrs = DMA_ATTR_WRITE_COMBINE
| DMA_ATTR_NO_KERNEL_MAPPING;
if (!is_iommu_enabled(dev))
vs_obj->dma_attrs |= DMA_ATTR_FORCE_CONTIGUOUS;
nr_pages = vs_obj->size >> PAGE_SHIFT;
vs_obj->pages = kvmalloc_array(nr_pages, sizeof(struct page *),
GFP_KERNEL | __GFP_ZERO);
if (!vs_obj->pages) {
DRM_DEV_ERROR(dev->dev, "failed to allocate pages.\n");
return -ENOMEM;
}
vs_obj->cookie = dma_alloc_attrs(to_dma_dev(dev), vs_obj->size,
&vs_obj->dma_addr, GFP_KERNEL,
vs_obj->dma_attrs);
DRM_DEV_DEBUG(dev->dev,"Allocated coherent memory, vaddr: 0x%0llX, paddr: 0x%0llX, size: %lu\n",
(u64)vs_obj->cookie,vs_obj->dma_addr,vs_obj->size);
if (!vs_obj->cookie) {
#ifdef CONFIG_VERISILICON_MMU
ret = get_pages(nr_pages, vs_obj);
if (ret) {
DRM_DEV_ERROR(dev->dev, "fail to allocate buffer.\n");
goto err_free;
}
#else
DRM_DEV_ERROR(dev->dev, "failed to allocate buffer.\n");
goto err_free;
#endif
}
#ifdef CONFIG_VERISILICON_MMU
/* MMU map*/
if (!priv->mmu) {
DRM_DEV_ERROR(dev->dev, "invalid mmu.\n");
ret = -EINVAL;
goto err_mem_free;
}
/* mmu for ree driver */
if (!vs_obj->get_pages)
ret = dc_mmu_map_memory(priv->mmu, (u64)vs_obj->dma_addr,
nr_pages, &vs_obj->iova, true, false);
else
ret = dc_mmu_map_memory(priv->mmu, (u64)vs_obj->pages,
nr_pages, &vs_obj->iova, false, false);
if (ret) {
DRM_DEV_ERROR(dev->dev, "failed to do mmu map.\n");
goto err_mem_free;
}
#else
vs_obj->iova = vs_obj->dma_addr;
#endif
if (!vs_obj->get_pages) {
ret = dma_get_sgtable_attrs(to_dma_dev(dev), &sgt,
vs_obj->cookie, vs_obj->dma_addr,
vs_obj->size, vs_obj->dma_attrs);
if (ret < 0) {
DRM_DEV_ERROR(dev->dev, "failed to get sgtable.\n");
goto err_mem_free;
}
if (drm_prime_sg_to_page_array(&sgt, vs_obj->pages, nr_pages)) {
DRM_DEV_ERROR(dev->dev, "invalid sgtable.\n");
ret = -EINVAL;
goto err_sgt_free;
}
sg_free_table(&sgt);
}
return 0;
err_sgt_free:
sg_free_table(&sgt);
err_mem_free:
if (!vs_obj->get_pages)
dma_free_attrs(to_dma_dev(dev), vs_obj->size, vs_obj->cookie,
vs_obj->dma_addr, vs_obj->dma_attrs);
else
put_pages(nr_pages, vs_obj);
err_free:
kvfree(vs_obj->pages);
return ret;
}
static void vs_gem_free_buf(struct vs_gem_object *vs_obj)
{
struct drm_device *dev = vs_obj->base.dev;
#ifdef CONFIG_VERISILICON_MMU
struct vs_drm_private *priv = dev->dev_private;
unsigned int nr_pages;
#endif
if ((!vs_obj->get_pages) && (!vs_obj->dma_addr)) {
DRM_DEV_DEBUG_KMS(dev->dev, "dma_addr is invalid.\n");
return;
}
#ifdef CONFIG_VERISILICON_MMU
if (!priv->mmu) {
DRM_DEV_ERROR(dev->dev, "invalid mmu.\n");
return;
}
nr_pages = vs_obj->size >> PAGE_SHIFT;
dc_mmu_unmap_memory(priv->mmu, vs_obj->iova, nr_pages);
#endif
if (!vs_obj->get_pages)
dma_free_attrs(to_dma_dev(dev), vs_obj->size, vs_obj->cookie,
(dma_addr_t)vs_obj->dma_addr,
vs_obj->dma_attrs);
else
put_pages(vs_obj->size >> PAGE_SHIFT, vs_obj);
kvfree(vs_obj->pages);
}
static void vs_gem_free_object(struct drm_gem_object *obj)
{
struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
if (obj->import_attach)
drm_prime_gem_destroy(obj, vs_obj->sgt);
else
vs_gem_free_buf(vs_obj);
drm_gem_object_release(obj);
kfree(vs_obj);
}
static struct vs_gem_object *vs_gem_alloc_object(struct drm_device *dev,
size_t size)
{
struct vs_gem_object *vs_obj;
struct drm_gem_object *obj;
int ret;
vs_obj = kzalloc(sizeof(*vs_obj), GFP_KERNEL);
if (!vs_obj)
return ERR_PTR(-ENOMEM);
vs_obj->size = size;
obj = &vs_obj->base;
ret = drm_gem_object_init(dev, obj, size);
if (ret)
goto err_free;
vs_obj->base.funcs = &vs_gem_default_funcs;
ret = drm_gem_create_mmap_offset(obj);
if (ret) {
drm_gem_object_release(obj);
goto err_free;
}
return vs_obj;
err_free:
kfree(vs_obj);
return ERR_PTR(ret);
}
struct vs_gem_object *vs_gem_create_object(struct drm_device *dev,
size_t size)
{
struct vs_gem_object *vs_obj;
int ret;
size = PAGE_ALIGN(size);
vs_obj = vs_gem_alloc_object(dev, size);
if (IS_ERR(vs_obj))
return vs_obj;
ret = vs_gem_alloc_buf(vs_obj);
if (ret) {
drm_gem_object_release(&vs_obj->base);
kfree(vs_obj);
return ERR_PTR(ret);
}
return vs_obj;
}
static struct vs_gem_object *vs_gem_create_with_handle(struct drm_device *dev,
struct drm_file *file,
size_t size,
unsigned int *handle)
{
struct vs_gem_object *vs_obj;
struct drm_gem_object *obj;
int ret;
vs_obj = vs_gem_create_object(dev, size);
if (IS_ERR(vs_obj))
return vs_obj;
obj = &vs_obj->base;
ret = drm_gem_handle_create(file, obj, handle);
#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE
drm_gem_object_put(obj);
#else
drm_gem_object_put_unlocked(obj);
#endif
if (ret)
return ERR_PTR(ret);
return vs_obj;
}
static int vs_gem_mmap_obj(struct drm_gem_object *obj,
struct vm_area_struct *vma)
{
struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
struct drm_device *drm_dev = vs_obj->base.dev;
unsigned long vm_size;
int ret = 0;
vm_size = vma->vm_end - vma->vm_start;
if (vm_size > vs_obj->size)
return -EINVAL;
vma->vm_pgoff = 0;
if (!vs_obj->get_pages) {
vm_flags_clear(vma, VM_PFNMAP);
ret = dma_mmap_attrs(to_dma_dev(drm_dev), vma, vs_obj->cookie,
vs_obj->dma_addr, vs_obj->size,
vs_obj->dma_attrs);
} else {
u32 i, nr_pages, pfn = 0U;
unsigned long start;
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
vm_flags_set(vma, VM_IO | VM_DONTCOPY | VM_DONTEXPAND |
VM_DONTDUMP);
start = vma->vm_start;
vm_size = PAGE_ALIGN(vm_size);
nr_pages = vm_size >> PAGE_SHIFT;
for (i = 0; i < nr_pages; i++) {
pfn = page_to_pfn(vs_obj->pages[i]);
ret = remap_pfn_range(vma, start, pfn, PAGE_SIZE,
vma->vm_page_prot);
if (ret < 0)
break;
start += PAGE_SIZE;
}
}
if (ret)
drm_gem_vm_close(vma);
return ret;
}
struct sg_table *vs_gem_prime_get_sg_table(struct drm_gem_object *obj)
{
struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE
return drm_prime_pages_to_sg(obj->dev, vs_obj->pages,
vs_obj->size >> PAGE_SHIFT);
#else
return drm_prime_pages_to_sg(vs_obj->pages, vs_obj->size >> PAGE_SHIFT);
#endif
}
int vs_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map)
{
struct vs_gem_object *vs_obj = to_vs_gem_object(obj);
void * vaddr = vs_obj->dma_attrs & DMA_ATTR_NO_KERNEL_MAPPING ?
page_address(vs_obj->cookie) : vs_obj->cookie;
iosys_map_set_vaddr(map, vaddr);
return 0;
}
void vs_gem_prime_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
{
/* Nothing to do */
}
static const struct vm_operations_struct vs_vm_ops = {
.open = drm_gem_vm_open,
.close = drm_gem_vm_close,
};
static const struct drm_gem_object_funcs vs_gem_default_funcs = {
.free = vs_gem_free_object,
.get_sg_table = vs_gem_prime_get_sg_table,
.vmap = vs_gem_prime_vmap,
.vunmap = vs_gem_prime_vunmap,
.vm_ops = &vs_vm_ops,
.mmap = vs_gem_mmap_obj,
};
int vs_gem_dumb_create(struct drm_file *file,
struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
struct vs_drm_private *priv = dev->dev_private;
struct vs_gem_object *vs_obj;
unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
if (args->bpp % 10)
args->pitch = ALIGN(pitch, priv->pitch_alignment);
else
/* for costum 10bit format with no bit gaps */
args->pitch = pitch;
args->size = PAGE_ALIGN(args->pitch * args->height);
vs_obj = vs_gem_create_with_handle(dev, file, args->size,
&args->handle);
return PTR_ERR_OR_ZERO(vs_obj);
}
struct drm_gem_object *vs_gem_prime_import(struct drm_device *dev,
struct dma_buf *dma_buf)
{
return drm_gem_prime_import_dev(dev, dma_buf, to_dma_dev(dev));
}
struct drm_gem_object *
vs_gem_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach,
struct sg_table *sgt)
{
struct vs_gem_object *vs_obj;
int npages;
int ret;
struct scatterlist *s;
u32 i;
dma_addr_t expected;
size_t size = attach->dmabuf->size;
#ifdef CONFIG_VERISILICON_MMU
u32 iova;
struct vs_drm_private *priv = dev->dev_private;
if (!priv->mmu) {
DRM_ERROR("invalid mmu.\n");
ret = -EINVAL;
return ERR_PTR(ret);
}
#endif
size = PAGE_ALIGN(size);
vs_obj = vs_gem_alloc_object(dev, size);
if (IS_ERR(vs_obj))
return ERR_CAST(vs_obj);
expected = sg_dma_address(sgt->sgl);
for_each_sg(sgt->sgl, s, sgt->nents, i) {
if (sg_dma_address(s) != expected) {
#ifndef CONFIG_VERISILICON_MMU
DRM_ERROR("sg_table is not contiguous");
ret = -EINVAL;
goto err;
#endif
}
if (sg_dma_len(s) & (PAGE_SIZE-1)) {
ret = -EINVAL;
goto err;
}
#ifdef CONFIG_VERISILICON_MMU
iova = 0;
npages = sg_dma_len(s) >> PAGE_SHIFT;
ret = dc_mmu_map_memory(priv->mmu, (u64)sg_dma_address(s),
npages, &iova, true, false);
if (ret) {
DRM_ERROR("failed to do mmu map.\n");
goto err;
}
if (i == 0)
vs_obj->iova = iova;
#else
if (i == 0)
vs_obj->iova = sg_dma_address(s);
#endif
expected = sg_dma_address(s) + sg_dma_len(s);
}
vs_obj->dma_addr = sg_dma_address(sgt->sgl);
npages = vs_obj->size >> PAGE_SHIFT;
vs_obj->pages = kvmalloc_array(npages, sizeof(struct page *),
GFP_KERNEL);
if (!vs_obj->pages) {
ret = -ENOMEM;
goto err;
}
//ret = drm_prime_sg_to_dma_addr_array(sgt, vs_obj->dma_addr,npages);
ret = drm_prime_sg_to_page_array(sgt, vs_obj->pages, npages);
if (ret)
goto err_free_page;
vs_obj->sgt = sgt;
return &vs_obj->base;
err_free_page:
kvfree(vs_obj->pages);
err:
vs_gem_free_object(&vs_obj->base);
return ERR_PTR(ret);
}
int vs_gem_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct drm_gem_object *obj;
int ret;
ret = drm_gem_mmap(filp, vma);
if (ret)
return ret;
obj = vma->vm_private_data;
if (obj->import_attach)
return dma_buf_mmap(obj->dma_buf, vma, 0);
return vs_gem_mmap_obj(obj, vma);
}

View file

@ -0,0 +1,74 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#ifndef __VS_GEM_H__
#define __VS_GEM_H__
#include <linux/dma-buf.h>
#include <drm/drm_gem.h>
#include "vs_drv.h"
#if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE
#include <drm/drm_prime.h>
#endif
/*
*
* @base: drm gem object.
* @size: size requested from user
* @cookie: cookie returned by dma_alloc_attrs
* - not kernel virtual address with DMA_ATTR_NO_KERNEL_MAPPING
* @dma_addr: bus address(accessed by dma) to allocated memory region.
* - this address could be physical address without IOMMU and
* device address with IOMMU.
* @dma_attrs: attribute for DMA API
* @get_pages: flag for manually applying for non-contiguous memory.
* @pages: Array of backing pages.
* @sgt: Imported sg_table.
*
*/
struct vs_gem_object {
struct drm_gem_object base;
size_t size;
void *cookie;
dma_addr_t dma_addr;
u32 iova;
unsigned long dma_attrs;
bool get_pages;
struct page **pages;
struct sg_table *sgt;
};
static inline
struct vs_gem_object *to_vs_gem_object(struct drm_gem_object *obj)
{
return container_of(obj, struct vs_gem_object, base);
}
struct vs_gem_object *vs_gem_create_object(struct drm_device *dev,
size_t size);
int vs_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map);
void vs_gem_prime_vunmap(struct drm_gem_object *obj, struct iosys_map *map);
int vs_gem_dumb_create(struct drm_file *file_priv,
struct drm_device *drm,
struct drm_mode_create_dumb *args);
int vs_gem_mmap(struct file *filp, struct vm_area_struct *vma);
struct sg_table *vs_gem_prime_get_sg_table(struct drm_gem_object *obj);
struct drm_gem_object *vs_gem_prime_import(struct drm_device *dev,
struct dma_buf *dma_buf);
struct drm_gem_object *
vs_gem_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach,
struct sg_table *sgt);
#endif /* __VS_GEM_H__ */

View file

@ -0,0 +1,526 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#include <linux/media-bus-format.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_blend.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/vs_drm.h>
#include "vs_type.h"
#include "vs_crtc.h"
#include "vs_plane.h"
#include "vs_gem.h"
#include "vs_fb.h"
#include "vs_dc.h"
void vs_plane_destory(struct drm_plane *plane)
{
struct vs_plane *vs_plane = to_vs_plane(plane);
drm_plane_cleanup(plane);
kfree(vs_plane);
}
static void vs_plane_reset(struct drm_plane *plane)
{
struct vs_plane_state *state;
struct vs_plane *vs_plane = to_vs_plane(plane);
if (plane->state) {
__drm_atomic_helper_plane_destroy_state(plane->state);
state = to_vs_plane_state(plane->state);
kfree(state);
plane->state = NULL;
}
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (state == NULL)
return;
__drm_atomic_helper_plane_reset(plane, &state->base);
state->degamma = VS_DEGAMMA_DISABLE;
state->degamma_changed = false;
state->base.zpos = vs_plane->id;
memset(&state->status, 0, sizeof(state->status));
}
static void _vs_plane_duplicate_blob(struct vs_plane_state *state,
struct vs_plane_state *ori_state)
{
state->watermark = ori_state->watermark;
state->color_mgmt = ori_state->color_mgmt;
state->roi = ori_state->roi;
if (state->watermark)
drm_property_blob_get(state->watermark);
if (state->color_mgmt)
drm_property_blob_get(state->color_mgmt);
if (state->roi)
drm_property_blob_get(state->roi);
}
static int
_vs_plane_set_property_blob_from_id(struct drm_device *dev,
struct drm_property_blob **blob,
uint64_t blob_id,
size_t expected_size)
{
struct drm_property_blob *new_blob = NULL;
if (blob_id) {
new_blob = drm_property_lookup_blob(dev, blob_id);
if (new_blob == NULL)
return -EINVAL;
if (new_blob->length != expected_size) {
drm_property_blob_put(new_blob);
return -EINVAL;
}
}
drm_property_replace_blob(blob, new_blob);
drm_property_blob_put(new_blob);
return 0;
}
static struct drm_plane_state *
vs_plane_atomic_duplicate_state(struct drm_plane *plane)
{
struct vs_plane_state *ori_state;
struct vs_plane_state *state;
if (WARN_ON(!plane->state))
return NULL;
ori_state = to_vs_plane_state(plane->state);
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return NULL;
__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
state->degamma = ori_state->degamma;
state->degamma_changed = ori_state->degamma_changed;
_vs_plane_duplicate_blob(state, ori_state);
memcpy(&state->status, &ori_state->status, sizeof(ori_state->status));
return &state->base;
}
static void vs_plane_atomic_destroy_state(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
__drm_atomic_helper_plane_destroy_state(state);
drm_property_blob_put(vs_plane_state->watermark);
drm_property_blob_put(vs_plane_state->color_mgmt);
drm_property_blob_put(vs_plane_state->roi);
kfree(vs_plane_state);
}
static int vs_plane_atomic_set_property(struct drm_plane *plane,
struct drm_plane_state *state,
struct drm_property *property,
uint64_t val)
{
struct drm_device *dev = plane->dev;
struct vs_plane *vs_plane = to_vs_plane(plane);
struct vs_plane_state *vs_plane_state = to_vs_plane_state(state);
int ret = 0;
if (property == vs_plane->degamma_mode) {
if (vs_plane_state->degamma != val) {
vs_plane_state->degamma = val;
vs_plane_state->degamma_changed = true;
} else {
vs_plane_state->degamma_changed = false;
}
} else if (property == vs_plane->watermark_prop) {
ret = _vs_plane_set_property_blob_from_id(dev,
&vs_plane_state->watermark,
val, sizeof(struct drm_vs_watermark));
return ret;
} else if (property == vs_plane->color_mgmt_prop) {
ret = _vs_plane_set_property_blob_from_id(dev,
&vs_plane_state->color_mgmt,
val, sizeof(struct drm_vs_color_mgmt));
return ret;
} else if (property == vs_plane->roi_prop) {
ret = _vs_plane_set_property_blob_from_id(dev,
&vs_plane_state->roi,
val, sizeof(struct drm_vs_roi));
return ret;
} else {
return -EINVAL;
}
return 0;
}
static int vs_plane_atomic_get_property(struct drm_plane *plane,
const struct drm_plane_state *state,
struct drm_property *property,
uint64_t *val)
{
struct vs_plane *vs_plane = to_vs_plane(plane);
const struct vs_plane_state *vs_plane_state =
container_of(state, const struct vs_plane_state, base);
if (property == vs_plane->degamma_mode)
*val = vs_plane_state->degamma;
else if (property == vs_plane->watermark_prop)
*val = (vs_plane_state->watermark) ?
vs_plane_state->watermark->base.id : 0;
else if (property == vs_plane->color_mgmt_prop)
*val = (vs_plane_state->color_mgmt) ?
vs_plane_state->color_mgmt->base.id : 0;
else if (property == vs_plane->roi_prop)
*val = (vs_plane_state->roi) ?
vs_plane_state->roi->base.id : 0;
else
return -EINVAL;
return 0;
}
static bool vs_format_mod_supported(struct drm_plane *plane,
uint32_t format,
uint64_t modifier)
{
int i;
/*
* We always have to allow these modifiers:
* 1. Core DRM checks for LINEAR support if userspace does not provide modifiers.
* 2. Not passing any modifiers is the same as explicitly passing INVALID.
*/
if (modifier == DRM_FORMAT_MOD_LINEAR) {
return true;
}
/* Check that the modifier is on the list of the plane's supported modifiers. */
for (i = 0; i < plane->modifier_count; i++) {
if (modifier == plane->modifiers[i])
break;
}
if (i == plane->modifier_count)
return false;
return true;
}
const struct drm_plane_funcs vs_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = vs_plane_destory,
.reset = vs_plane_reset,
.atomic_duplicate_state = vs_plane_atomic_duplicate_state,
.atomic_destroy_state = vs_plane_atomic_destroy_state,
.atomic_set_property = vs_plane_atomic_set_property,
.atomic_get_property = vs_plane_atomic_get_property,
.format_mod_supported = vs_format_mod_supported,
};
static unsigned char vs_get_plane_number(struct drm_framebuffer *fb)
{
const struct drm_format_info *info;
if (!fb)
return 0;
info = drm_format_info(fb->format->format);
if (!info || info->num_planes > MAX_NUM_PLANES)
return 0;
return info->num_planes;
}
static int vs_plane_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
plane);
struct vs_plane *vs_plane = to_vs_plane(plane);
struct drm_framebuffer *fb = new_plane_state->fb;
struct drm_crtc *crtc = new_plane_state->crtc;
struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
if (!crtc || !fb)
return 0;
//return vs_plane->funcs->check(vs_crtc->dev, vs_plane, new_plane_state);
return vs_plane->funcs->check(vs_crtc->dev, plane, state);
}
static void vs_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
plane);
unsigned char i, num_planes;
struct drm_framebuffer *fb;
struct vs_plane *vs_plane = to_vs_plane(plane);
//struct drm_plane_state *state = plane->state;
struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
//struct drm_format_name_buf *name = &plane_state->status.format_name;
if (!new_state->fb || !new_state->crtc)
return;
fb = new_state->fb;
num_planes = vs_get_plane_number(fb);
for (i = 0; i < num_planes; i++) {
struct vs_gem_object *vs_obj;
vs_obj = vs_fb_get_gem_obj(fb, i);
vs_plane->dma_addr[i] = vs_obj->iova + fb->offsets[i];
}
plane_state->status.src = drm_plane_state_src(new_state);
plane_state->status.dest = drm_plane_state_dest(new_state);
//drm_get_format_name(fb->format->format, name);
vs_plane->funcs->update(vs_crtc->dev, vs_plane, plane, state);
}
static void vs_plane_atomic_disable(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
plane);
struct vs_plane *vs_plane = to_vs_plane(plane);
struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
vs_plane->funcs->disable(vs_crtc->dev, vs_plane, old_state);
}
static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
plane);
struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
plane);
unsigned char i, num_planes;
struct drm_framebuffer *fb;
struct vs_plane *vs_plane = to_vs_plane(plane);
struct vs_crtc *vs_crtc = to_vs_crtc(new_state->crtc);
struct vs_plane_state *plane_state = to_vs_plane_state(new_state);
struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
if (!new_state->fb || !new_state->crtc)
return;
fb = new_state->fb;
num_planes = vs_get_plane_number(fb);
for (i = 0; i < num_planes; i++) {
struct vs_gem_object *vs_obj;
vs_obj = vs_fb_get_gem_obj(fb, i);
vs_plane->dma_addr[i] = vs_obj->iova + fb->offsets[i];
}
plane_state->status.src = drm_plane_state_src(new_state);
plane_state->status.dest = drm_plane_state_dest(new_state);
vs_dc_update_cursor_plane(dc, vs_plane, plane, state);
}
static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
plane);
struct vs_plane *vs_plane = to_vs_plane(plane);
struct vs_crtc *vs_crtc = to_vs_crtc(old_state->crtc);
struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
vs_dc_disable_cursor_plane(dc, vs_plane, old_state);
}
static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
plane);
unsigned char i, num_planes;
struct drm_framebuffer *fb = new_plane_state->fb;
struct drm_crtc *crtc = new_plane_state->crtc;
struct vs_crtc *vs_crtc = to_vs_crtc(crtc);
struct vs_dc *dc = dev_get_drvdata(vs_crtc->dev);
struct vs_plane_state *plane_state = to_vs_plane_state(new_plane_state);
if (!crtc || !fb)
return 0;
return vs_dc_check_cursor_plane(dc, plane, state);
}
const struct drm_plane_helper_funcs primary_plane_helpers = {
.atomic_check = vs_plane_atomic_check,
.atomic_update = vs_plane_atomic_update,
.atomic_disable = vs_plane_atomic_disable,
};
const struct drm_plane_helper_funcs overlay_plane_helpers = {
.atomic_check = vs_plane_atomic_check,
.atomic_update = vs_plane_atomic_update,
.atomic_disable = vs_plane_atomic_disable,
};
const struct drm_plane_helper_funcs cursor_plane_helpers = {
.atomic_check = vs_cursor_plane_atomic_check,
.atomic_update = vs_cursor_plane_atomic_update,
.atomic_disable = vs_cursor_plane_atomic_disable,
};
static const struct drm_prop_enum_list vs_degamma_mode_enum_list[] = {
{ VS_DEGAMMA_DISABLE, "disabled" },
{ VS_DEGAMMA_BT709, "preset degamma for BT709" },
{ VS_DEGAMMA_BT2020, "preset degamma for BT2020" },
};
struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
struct vs_plane_info *info,
unsigned int layer_num,
unsigned int possible_crtcs)
{
struct vs_plane *plane;
int ret;
if (!info)
return NULL;
plane = kzalloc(sizeof(struct vs_plane), GFP_KERNEL);
if (!plane)
return NULL;
ret = drm_universal_plane_init(drm_dev, &plane->base, possible_crtcs,
&vs_plane_funcs, info->formats,
info->num_formats, info->modifiers, info->type,
info->name ? info->name : NULL);
if (ret)
goto err_free_plane;
if (info->type == DRM_PLANE_TYPE_PRIMARY)
drm_plane_helper_add(&plane->base, &primary_plane_helpers);
else if (info->type == DRM_PLANE_TYPE_CURSOR)
drm_plane_helper_add(&plane->base, &cursor_plane_helpers);
else
drm_plane_helper_add(&plane->base, &overlay_plane_helpers);
/* Set up the plane properties */
if (info->degamma_size) {
plane->degamma_mode = drm_property_create_enum(drm_dev, 0,
"DEGAMMA_MODE",
vs_degamma_mode_enum_list,
ARRAY_SIZE(vs_degamma_mode_enum_list));
if (!plane->degamma_mode)
goto error_cleanup_plane;
drm_object_attach_property(&plane->base.base,
plane->degamma_mode,
VS_DEGAMMA_DISABLE);
}
if (info->rotation) {
ret = drm_plane_create_rotation_property(&plane->base,
DRM_MODE_ROTATE_0,
info->rotation);
if (ret)
goto error_cleanup_plane;
}
if (info->blend_mode) {
ret = drm_plane_create_blend_mode_property(&plane->base,
info->blend_mode);
if (ret)
goto error_cleanup_plane;
ret = drm_plane_create_alpha_property(&plane->base);
if (ret)
goto error_cleanup_plane;
}
if (info->color_encoding) {
ret = drm_plane_create_color_properties(&plane->base,
info->color_encoding,
BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
DRM_COLOR_YCBCR_BT709,
DRM_COLOR_YCBCR_LIMITED_RANGE);
if (ret)
goto error_cleanup_plane;
}
if (info->zpos != 255) {
ret = drm_plane_create_zpos_property(&plane->base, info->zpos, 0, layer_num - 1);
if (ret)
goto error_cleanup_plane;
}
#if KERNEL_VERSION(5, 8, 0) <= LINUX_VERSION_CODE
else {
ret = drm_plane_create_zpos_immutable_property(&plane->base,
info->zpos);
if (ret)
goto error_cleanup_plane;
}
#endif
if (info->watermark) {
plane->watermark_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
"WATERMARK", 0);
if (!plane->watermark_prop)
goto error_cleanup_plane;
drm_object_attach_property(&plane->base.base, plane->watermark_prop, 0);
}
if (info->color_mgmt) {
plane->color_mgmt_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
"COLOR_CONFIG", 0);
if (!plane->color_mgmt_prop)
goto error_cleanup_plane;
drm_object_attach_property(&plane->base.base, plane->color_mgmt_prop, 0);
}
if (info->roi) {
plane->roi_prop = drm_property_create(drm_dev, DRM_MODE_PROP_BLOB,
"ROI", 0);
if (!plane->roi_prop)
goto error_cleanup_plane;
drm_object_attach_property(&plane->base.base, plane->roi_prop, 0);
}
return plane;
error_cleanup_plane:
drm_plane_cleanup(&plane->base);
err_free_plane:
kfree(plane);
return NULL;
}

View file

@ -0,0 +1,81 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#ifndef __VS_PLANE_H__
#define __VS_PLANE_H__
#include <drm/drm_plane_helper.h>
#include <drm/drm_fourcc.h>
#include "vs_type.h"
#include "vs_fb.h"
#define MAX_NUM_PLANES 3 /* colour format plane */
struct vs_plane;
struct vs_plane_funcs {
void (*update)(struct device *dev, struct vs_plane *plane, struct drm_plane *drm_plane,
struct drm_atomic_state *state);
void (*disable)(struct device *dev, struct vs_plane *plane,
struct drm_plane_state *old_state);
int (*check)(struct device *dev, struct drm_plane *plane,
struct drm_atomic_state *state);
};
struct vs_plane_status {
u32 tile_mode;
struct drm_rect src;
struct drm_rect dest;
//struct drm_format_name_buf format_name;
};
struct vs_plane_state {
struct drm_plane_state base;
struct vs_plane_status status; /* for debugfs */
dma_addr_t dma_addr[MAX_NUM_PLANES];
struct drm_property_blob *watermark;
struct drm_property_blob *color_mgmt;
struct drm_property_blob *roi;
u32 degamma;
bool degamma_changed;
};
struct vs_plane {
struct drm_plane base;
u8 id;
dma_addr_t dma_addr[MAX_NUM_PLANES];
struct drm_property *degamma_mode;
struct drm_property *watermark_prop;
struct drm_property *color_mgmt_prop;
struct drm_property *roi_prop;
const struct vs_plane_funcs *funcs;
};
void vs_plane_destory(struct drm_plane *plane);
struct vs_plane *vs_plane_create(struct drm_device *drm_dev,
struct vs_plane_info *info,
unsigned int layer_num,
unsigned int possible_crtcs);
static inline struct vs_plane *to_vs_plane(struct drm_plane *plane)
{
return container_of(plane, struct vs_plane, base);
}
static inline struct vs_plane_state *
to_vs_plane_state(struct drm_plane_state *state)
{
return container_of(state, struct vs_plane_state, base);
}
extern void sifive_l2_flush64_range(unsigned long start, unsigned long len);
#endif /* __VS_PLANE_H__ */

View file

@ -0,0 +1,312 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#include <linux/version.h>
#include <linux/component.h>
#include <linux/of_device.h>
#if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE
#include <linux/module.h>
#include <drm/drm_bridge.h>
#else
#include <drm/drmP.h>
#endif
#include <drm/drm_crtc_helper.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_of.h>
#include <linux/regmap.h>
#include <linux/media-bus-format.h>
#include <linux/mfd/syscon.h>
#include "vs_crtc.h"
#include "vs_simple_enc.h"
static const struct simple_encoder_priv hdmi_priv = {
.encoder_type = DRM_MODE_ENCODER_TMDS
};
static const struct simple_encoder_priv dsi_priv = {
.encoder_type = DRM_MODE_ENCODER_DSI
};
static const struct drm_encoder_funcs encoder_funcs = {
.destroy = drm_encoder_cleanup
};
static inline struct simple_encoder *to_simple_encoder(struct drm_encoder *enc)
{
return container_of(enc, struct simple_encoder, encoder);
}
#if 0
static int encoder_parse_dt(struct device *dev)
{
struct simple_encoder *simple = dev_get_drvdata(dev);
int ret = 0;
int cnt, i;
u32 *vals;
u32 *masks;
simple->dss_regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
"verisilicon,dss-syscon");
if (IS_ERR(simple->dss_regmap)) {
if (PTR_ERR(simple->dss_regmap) != -ENODEV) {
dev_err(dev, "failed to get dss-syscon\n");
ret = PTR_ERR(simple->dss_regmap);
goto err;
}
simple->dss_regmap = NULL;
goto err;
}
cnt = of_property_count_elems_of_size(dev->of_node,
"verisilicon,mux-mask", 4);
if (!cnt) {
ret = cnt;
goto err;
}
simple->dss_regdatas = devm_kzalloc(dev,
sizeof(*simple->dss_regdatas) * cnt, GFP_KERNEL);
masks = kcalloc(cnt, sizeof(*masks), GFP_KERNEL);
if (!masks) {
ret = -ENOMEM;
goto err;
}
vals = kcalloc(cnt, sizeof(*vals), GFP_KERNEL);
if (!vals) {
ret = -ENOMEM;
goto err_free_masks;
}
ret = of_property_read_u32_array(
dev->of_node, "verisilicon,mux-mask", masks, cnt);
if (ret)
goto err_free_vals;
ret = of_property_read_u32_array(
dev->of_node, "verisilicon,mux-val", vals, cnt);
if (ret)
goto err_free_vals;
for (i = 0; i < cnt; i++) {
simple->dss_regdatas[i].mask = masks[i];
simple->dss_regdatas[i].value = vals[i];
}
err_free_vals:
kfree(vals);
err_free_masks:
kfree(masks);
err:
return ret;
}
#endif
#define DOM_VOUT_SYSCON_8 0x8U
#define U0_LCD_DATA_MAPPING_DPI_DP_SEL_SHIFT 0x2U
#define U0_LCD_DATA_MAPPING_DPI_DP_SEL_MASK 0x4U
#define DOM_VOUT_SYSCON_4 0x4U
#define U0_DISPLAY_PANEL_MUX_PANEL_SEL_SHIFT 0x14U
#define U0_DISPLAY_PANEL_MUX_PANEL_SEL_MASK 0x100000U
void encoder_atomic_enable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
return;
}
int encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct vs_crtc_state *vs_crtc_state = to_vs_crtc_state(crtc_state);
struct drm_connector *connector = conn_state->connector;
int ret = 0;
#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE
struct drm_bridge *first_bridge = drm_bridge_chain_get_first_bridge(encoder);
struct drm_bridge_state *bridge_state = ERR_PTR(-EINVAL);
#endif
vs_crtc_state->encoder_type = encoder->encoder_type;
#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE
if (first_bridge && first_bridge->funcs->atomic_duplicate_state)
bridge_state = drm_atomic_get_bridge_state(
crtc_state->state, first_bridge);
if (IS_ERR(bridge_state)) {
if (connector->display_info.num_bus_formats)
vs_crtc_state->output_fmt = connector->display_info.bus_formats[0];
else
vs_crtc_state->output_fmt = MEDIA_BUS_FMT_FIXED;
} else {
vs_crtc_state->output_fmt = bridge_state->input_bus_cfg.format;
}
#else
if (connector->display_info.num_bus_formats)
vs_crtc_state->output_fmt = connector->display_info.bus_formats[0];
else
vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
#endif
switch (vs_crtc_state->output_fmt) {
case MEDIA_BUS_FMT_FIXED:
case MEDIA_BUS_FMT_RGB565_1X16:
case MEDIA_BUS_FMT_RGB666_1X18:
case MEDIA_BUS_FMT_RGB888_1X24:
case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
case MEDIA_BUS_FMT_RGB101010_1X30:
case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
case MEDIA_BUS_FMT_UYVY8_1X16:
case MEDIA_BUS_FMT_YUV8_1X24:
case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
case MEDIA_BUS_FMT_UYVY10_1X20:
case MEDIA_BUS_FMT_YUV10_1X30:
ret = 0;
break;
default:
ret = -EINVAL;
break;
}
/* If MEDIA_BUS_FMT_FIXED, set it to default value */
if (vs_crtc_state->output_fmt == MEDIA_BUS_FMT_FIXED)
vs_crtc_state->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
return ret;
}
static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
.atomic_enable = encoder_atomic_enable,
.atomic_check = encoder_atomic_check,
};
static int encoder_bind(struct device *dev, struct device *master, void *data)
{
struct drm_device *drm_dev = data;
struct simple_encoder *simple = dev_get_drvdata(dev);
struct drm_encoder *encoder;
struct drm_bridge *bridge;
int ret;
#ifdef CONFIG_STARFIVE_DSI
struct drm_panel *tmp_panel;
#endif
encoder = &simple->encoder;
/* Encoder. */
dev_info(dev,"encoder_bind begin\n");
ret = drm_encoder_init(drm_dev, encoder, &encoder_funcs,
simple->priv->encoder_type, NULL);
if (ret)
return ret;
drm_encoder_helper_add(encoder, &encoder_helper_funcs);
encoder->possible_crtcs =
drm_of_find_possible_crtcs(drm_dev, dev->of_node);
encoder->possible_crtcs = 2;
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0,&tmp_panel, &bridge);
if (ret){
printk("no panel, %d\n",ret);
//dev_err_probe(dev, ret, "endpoint returns %d\n", ret);
goto err;
}
if (tmp_panel)
dev_err(dev, "found panel on endpoint\n");
#if KERNEL_VERSION(5, 7, 0) <= LINUX_VERSION_CODE
ret = drm_bridge_attach(encoder, bridge, NULL, 0);
#else
ret = drm_bridge_attach(encoder, bridge, NULL);
#endif
if (ret)
goto err;
dev_info(dev,"encoder_bind end\n");
return 0;
err:
drm_encoder_cleanup(encoder);
dev_info(dev,"encoder_bind error\n");
//return ret;
return 0;
}
static void encoder_unbind(struct device *dev, struct device *master,
void *data)
{
struct simple_encoder *simple = dev_get_drvdata(dev);
drm_encoder_cleanup(&simple->encoder);
}
static const struct component_ops encoder_component_ops = {
.bind = encoder_bind,
.unbind = encoder_unbind,
};
static const struct of_device_id simple_encoder_dt_match[] = {
{ .compatible = "verisilicon,rgb-encoder", .data = &hdmi_priv},
{ .compatible = "verisilicon,dp-encoder", .data = &hdmi_priv},
{ .compatible = "verisilicon,dsi-encoder", .data = &dsi_priv},
{},
};
MODULE_DEVICE_TABLE(of, simple_encoder_dt_match);
static int encoder_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct simple_encoder *simple;
simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
if (!simple)
return -ENOMEM;
simple->priv = of_device_get_match_data(dev);
simple->dev = dev;
dev_set_drvdata(dev, simple);
#if 0
ret = encoder_parse_dt(dev);
if (ret)
return ret;
#endif
return component_add(dev, &encoder_component_ops);
}
static int encoder_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
component_del(dev, &encoder_component_ops);
dev_set_drvdata(dev, NULL);
return 0;
}
struct platform_driver simple_encoder_driver = {
.probe = encoder_probe,
.remove = encoder_remove,
.driver = {
.name = "vs-simple-encoder",
.of_match_table = of_match_ptr(simple_encoder_dt_match),
},
};
MODULE_DESCRIPTION("Simple Encoder Driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#ifndef __VS_SIMPLE_ENC_H_
#define __VS_SIMPLE_ENC_H_
struct simple_encoder_priv {
unsigned char encoder_type;
};
struct dss_data {
u32 mask;
u32 value;
};
struct simple_encoder {
struct drm_encoder encoder;
struct device *dev;
const struct simple_encoder_priv *priv;
struct regmap *dss_regmap;
struct dss_data *dss_regdatas;
};
extern struct platform_driver simple_encoder_driver;
#endif /* __VS_SIMPLE_ENC_H_ */

View file

@ -0,0 +1,75 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#ifndef __VS_TYPE_H__
#define __VS_TYPE_H__
#include <linux/version.h>
#if KERNEL_VERSION(5, 5, 0) > LINUX_VERSION_CODE
#include <drm/drmP.h>
#endif
#include <drm/drm_plane.h>
#include <drm/drm_plane_helper.h>
struct vs_plane_info {
const char *name;
u8 id;
enum drm_plane_type type;
unsigned int num_formats;
const u32 *formats;
u8 num_modifiers;
const u64 *modifiers;
unsigned int min_width;
unsigned int min_height;
unsigned int max_width;
unsigned int max_height;
unsigned int rotation;
unsigned int blend_mode;
unsigned int color_encoding;
/* 0 means no de-gamma LUT */
unsigned int degamma_size;
int min_scale; /* 16.16 fixed point */
int max_scale; /* 16.16 fixed point */
/* default zorder value,
* and 255 means unsupported zorder capability
*/
u8 zpos;
bool watermark;
bool color_mgmt;
bool roi;
};
struct vs_dc_info {
const char *name;
u8 panel_num;
/* planes */
u8 plane_num;
const struct vs_plane_info *planes;
u8 layer_num;
unsigned int max_bpc;
unsigned int color_formats;
/* 0 means no gamma LUT */
u16 gamma_size;
u8 gamma_bits;
u16 pitch_alignment;
bool pipe_sync;
bool mmu_prefetch;
bool background;
bool panel_sync;
bool cap_dec;
};
#endif /* __VS_TYPE_H__ */

View file

@ -0,0 +1,363 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#include <linux/component.h>
#include <linux/of_platform.h>
#include <linux/media-bus-format.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_of.h>
#include <drm/drm_encoder.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_atomic_helper.h>
#include "vs_virtual.h"
#include "vs_dc.h"
#include "vs_gem.h"
static unsigned char __get_bpp(struct vs_virtual_display *vd)
{
if (vd->bus_format == MEDIA_BUS_FMT_RGB101010_1X30)
return 10;
return 8;
}
static void vd_dump_destroy(struct vs_virtual_display *vd)
{
if (vd->dump_blob.data) {
vunmap(vd->dump_blob.data);
vd->dump_blob.data = NULL;
}
vd->dump_blob.size = 0;
debugfs_remove(vd->dump_debugfs);
vd->dump_debugfs = NULL;
if (vd->dump_obj) {
#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE
drm_gem_object_put(&vd->dump_obj->base);
#else
drm_gem_object_put_unlocked(&vd->dump_obj->base);
#endif
vd->dump_obj = NULL;
}
}
static void vd_dump_create(struct vs_virtual_display *vd,
struct drm_display_mode *mode)
{
struct drm_device *drm_dev = vd->encoder.dev;
struct vs_dc *dc = dev_get_drvdata(vd->dc);
struct vs_gem_object *obj;
unsigned int pitch, size;
void *kvaddr;
char *name;
if (!dc->funcs)
return;
vd_dump_destroy(vd);
/* dump in 4bytes XRGB format */
pitch = mode->hdisplay * 4;
pitch = ALIGN(pitch, dc->hw.info->pitch_alignment);
size = PAGE_ALIGN(pitch * mode->vdisplay);
obj = vs_gem_create_object(drm_dev, size);
if (IS_ERR(obj))
return;
vd->dump_obj = obj;
vd->pitch = pitch;
kvaddr = vmap(obj->pages, obj->size >> PAGE_SHIFT, VM_MAP,
pgprot_writecombine(PAGE_KERNEL));
if (!kvaddr)
goto err;
vd->dump_blob.data = kvaddr;
vd->dump_blob.size = obj->size;
name = kasprintf(GFP_KERNEL, "%dx%d-XRGB-%d.raw",
mode->hdisplay, mode->vdisplay,
__get_bpp(vd));
if (!name)
goto err;
vd->dump_debugfs = debugfs_create_blob(name, 0444,
vd->connector.debugfs_entry,
&vd->dump_blob);
kfree(name);
return;
err:
vd_dump_destroy(vd);
}
static void vd_encoder_destroy(struct drm_encoder *encoder)
{
struct vs_virtual_display *vd;
drm_encoder_cleanup(encoder);
vd = to_virtual_display_with_encoder(encoder);
vd_dump_destroy(vd);
}
static const struct drm_encoder_funcs vd_encoder_funcs = {
.destroy = vd_encoder_destroy
};
static void vd_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct vs_virtual_display *vd;
vd = to_virtual_display_with_encoder(encoder);
vd_dump_create(vd, adjusted_mode);
}
static void vd_encoder_disable(struct drm_encoder *encoder)
{
struct vs_virtual_display *vd;
struct vs_dc *dc;
vd = to_virtual_display_with_encoder(encoder);
dc = dev_get_drvdata(vd->dc);
if (dc->funcs && dc->funcs->dump_disable)
dc->funcs->dump_disable(vd->dc);
}
static void vd_encoder_enable(struct drm_encoder *encoder)
{
struct vs_virtual_display *vd;
struct vs_dc *dc;
vd = to_virtual_display_with_encoder(encoder);
dc = dev_get_drvdata(vd->dc);
if (dc->funcs && dc->funcs->dump_enable && vd->dump_obj)
dc->funcs->dump_enable(vd->dc, vd->dump_obj->iova,
vd->pitch);
}
static const struct drm_encoder_helper_funcs vd_encoder_helper_funcs = {
.mode_set = vd_mode_set,
.enable = vd_encoder_enable,
.disable = vd_encoder_disable,
};
static int vd_get_modes(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_display_mode *mode = NULL;
unsigned int i;
static const struct display_mode {
int w, h;
} cvt_mode[] = {
{640, 480},
{720, 480},
{800, 600},
{1024, 768},
{1280, 720},
{1280, 1024},
{1400, 1050},
{1680, 1050},
{1600, 1200},
{1920, 1080},
{1920, 1200},
{3840, 2160}
};
for (i = 0; i < ARRAY_SIZE(cvt_mode); i++) {
mode = drm_cvt_mode(dev, cvt_mode[i].w, cvt_mode[i].h,
60, false, false, false);
drm_mode_probed_add(connector, mode);
}
return 0;
}
static struct drm_encoder *vd_best_encoder(struct drm_connector *connector)
{
struct vs_virtual_display *vd;
vd = to_virtual_display_with_connector(connector);
return &vd->encoder;
}
static enum drm_mode_status vd_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
return MODE_OK;
}
static const struct drm_connector_helper_funcs vd_connector_helper_funcs = {
.get_modes = vd_get_modes,
.mode_valid = vd_mode_valid,
.best_encoder = vd_best_encoder,
};
static void vd_connector_destroy(struct drm_connector *connector)
{
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
}
static enum drm_connector_status
vd_connector_detect(struct drm_connector *connector, bool force)
{
return connector_status_connected;
}
static const struct drm_connector_funcs vd_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = vd_connector_destroy,
.detect = vd_connector_detect,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.reset = drm_atomic_helper_connector_reset,
};
static int vd_bind(struct device *dev, struct device *master, void *data)
{
struct drm_device *drm_dev = data;
struct vs_virtual_display *vd = dev_get_drvdata(dev);
struct drm_encoder *encoder;
struct drm_connector *connector;
struct device_node *ep, *np;
struct platform_device *pdev;
int ret;
/* Encoder */
encoder = &vd->encoder;
ret = drm_encoder_init(drm_dev, encoder, &vd_encoder_funcs,
DRM_MODE_ENCODER_VIRTUAL, NULL);
if (ret)
return ret;
drm_encoder_helper_add(encoder, &vd_encoder_helper_funcs);
encoder->possible_crtcs =
drm_of_find_possible_crtcs(drm_dev, dev->of_node);
/* Connector */
connector = &vd->connector;
ret = drm_connector_init(drm_dev, connector, &vd_connector_funcs,
DRM_MODE_CONNECTOR_VIRTUAL);
if (ret)
goto connector_init_err;
drm_connector_helper_add(connector, &vd_connector_helper_funcs);
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
connector->dpms = DRM_MODE_DPMS_OFF;
connector->polled = DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT;
ret = drm_connector_register(connector);
if (ret)
goto connector_reg_err;
drm_display_info_set_bus_formats(&connector->display_info,
&vd->bus_format, 1);
/* attach */
ret = drm_connector_attach_encoder(connector, encoder);
if (ret)
goto attach_err;
ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1);
if (!ep) {
ret = -EINVAL;
goto attach_err;
}
np = of_graph_get_remote_port_parent(ep);
of_node_put(ep);
if (!np) {
ret = -EINVAL;
goto attach_err;
}
pdev = of_find_device_by_node(np);
of_node_put(np);
if (!pdev) {
ret = -EPROBE_DEFER;
goto attach_err;
}
get_device(&pdev->dev);
vd->dc = &pdev->dev;
return 0;
attach_err:
drm_connector_unregister(connector);
connector_reg_err:
drm_connector_cleanup(connector);
connector_init_err:
drm_encoder_cleanup(encoder);
return ret;
}
static void vd_unbind(struct device *dev, struct device *master, void *data)
{
struct vs_virtual_display *vd = dev_get_drvdata(dev);
drm_connector_unregister(&vd->connector);
drm_connector_cleanup(&vd->connector);
drm_encoder_cleanup(&vd->encoder);
}
const struct component_ops vd_component_ops = {
.bind = vd_bind,
.unbind = vd_unbind,
};
static const struct of_device_id vd_driver_dt_match[] = {
{ .compatible = "verisilicon,virtual_display", },
{},
};
MODULE_DEVICE_TABLE(of, vd_driver_dt_match);
static int vd_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct vs_virtual_display *vd;
unsigned char bpp;
vd = devm_kzalloc(dev, sizeof(*vd), GFP_KERNEL);
if (!vd)
return -ENOMEM;
vd->bus_format = MEDIA_BUS_FMT_RGB101010_1X30;
of_property_read_u8(dev->of_node, "bpp", &bpp);
if (bpp == 8)
vd->bus_format = MEDIA_BUS_FMT_RBG888_1X24;
dev_set_drvdata(dev, vd);
return component_add(dev, &vd_component_ops);
}
static int vd_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
component_del(dev, &vd_component_ops);
dev_set_drvdata(dev, NULL);
return 0;
}
struct platform_driver virtual_display_platform_driver = {
.probe = vd_probe,
.remove = vd_remove,
.driver = {
.name = "vs-virtual-display",
.of_match_table = of_match_ptr(vd_driver_dt_match),
},
};
MODULE_DESCRIPTION("VeriSilicon Virtual Display Driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,37 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#ifndef __VS_VIRTUAL_H_
#define __VS_VIRTUAL_H_
#include <linux/debugfs.h>
struct vs_virtual_display {
struct drm_encoder encoder;
struct drm_connector connector;
struct device *dc;
u32 bus_format;
struct dentry *dump_debugfs;
struct debugfs_blob_wrapper dump_blob;
struct vs_gem_object *dump_obj;
unsigned int pitch;
};
static inline struct vs_virtual_display *
to_virtual_display_with_connector(struct drm_connector *connector)
{
return container_of(connector, struct vs_virtual_display, connector);
}
static inline struct vs_virtual_display *
to_virtual_display_with_encoder(struct drm_encoder *encoder)
{
return container_of(encoder, struct vs_virtual_display, encoder);
}
extern struct platform_driver virtual_display_platform_driver;
#endif /* __VS_VIRTUAL_H_ */

View file

@ -98,5 +98,6 @@ source "drivers/phy/tegra/Kconfig"
source "drivers/phy/ti/Kconfig"
source "drivers/phy/intel/Kconfig"
source "drivers/phy/xilinx/Kconfig"
source "drivers/phy/m31/Kconfig"
endmenu

View file

@ -35,4 +35,5 @@ obj-y += allwinner/ \
sunplus/ \
tegra/ \
ti/ \
xilinx/
xilinx/ \
m31/

View file

@ -0,0 +1,396 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2022 StarFive Technology Co., Ltd.
*/
#ifndef __7110_M31_DPHY_H__
#define __7110_M31_DPHY_H__
#define AON_POWER_READY_N_WIDTH 0x1U
#define AON_POWER_READY_N_SHIFT 0x0U
#define AON_POWER_READY_N_MASK 0x1U
#define CFG_CKLANE_SET_WIDTH 0x5U
#define CFG_CKLANE_SET_SHIFT 0x1U
#define CFG_CKLANE_SET_MASK 0x3EU
#define CFG_DATABUS16_SEL_WIDTH 0x1U
#define CFG_DATABUS16_SEL_SHIFT 0x6U
#define CFG_DATABUS16_SEL_MASK 0x40U
#define CFG_DPDN_SWAP_WIDTH 0x5U
#define CFG_DPDN_SWAP_SHIFT 0x7U
#define CFG_DPDN_SWAP_MASK 0xF80U
#define CFG_L0_SWAP_SEL_WIDTH 0x3U
#define CFG_L0_SWAP_SEL_SHIFT 0xCU
#define CFG_L0_SWAP_SEL_MASK 0x7000U
#define CFG_L1_SWAP_SEL_WIDTH 0x3U
#define CFG_L1_SWAP_SEL_SHIFT 0xFU
#define CFG_L1_SWAP_SEL_MASK 0x38000U
#define CFG_L2_SWAP_SEL_WIDTH 0x3U
#define CFG_L2_SWAP_SEL_SHIFT 0x12U
#define CFG_L2_SWAP_SEL_MASK 0x1C0000U
#define CFG_L3_SWAP_SEL_WIDTH 0x3U
#define CFG_L3_SWAP_SEL_SHIFT 0x15U
#define CFG_L3_SWAP_SEL_MASK 0xE00000U
#define CFG_L4_SWAP_SEL_WIDTH 0x3U
#define CFG_L4_SWAP_SEL_SHIFT 0x18U
#define CFG_L4_SWAP_SEL_MASK 0x7000000U
#define MPOSV_31_0__WIDTH 0x20U
#define MPOSV_31_0__SHIFT 0x0U
#define MPOSV_31_0__MASK 0xFFFFFFFFU
#define MPOSV_46_32__WIDTH 0xFU
#define MPOSV_46_32__SHIFT 0x0U
#define MPOSV_46_32__MASK 0x7FFFU
#define RGS_CDTX_PLL_FM_CPLT_WIDTH 0x1U
#define RGS_CDTX_PLL_FM_CPLT_SHIFT 0xFU
#define RGS_CDTX_PLL_FM_CPLT_MASK 0x8000U
#define RGS_CDTX_PLL_FM_OVER_WIDTH 0x1U
#define RGS_CDTX_PLL_FM_OVER_SHIFT 0x10U
#define RGS_CDTX_PLL_FM_OVER_MASK 0x10000U
#define RGS_CDTX_PLL_FM_UNDER_WIDTH 0x1U
#define RGS_CDTX_PLL_FM_UNDER_SHIFT 0x11U
#define RGS_CDTX_PLL_FM_UNDER_MASK 0x20000U
#define RGS_CDTX_PLL_UNLOCK_WIDTH 0x1U
#define RGS_CDTX_PLL_UNLOCK_SHIFT 0x12U
#define RGS_CDTX_PLL_UNLOCK_MASK 0x40000U
#define RG_CDTX_L0N_HSTX_RES_WIDTH 0x5U
#define RG_CDTX_L0N_HSTX_RES_SHIFT 0x13U
#define RG_CDTX_L0N_HSTX_RES_MASK 0xF80000U
#define RG_CDTX_L0P_HSTX_RES_WIDTH 0x5U
#define RG_CDTX_L0P_HSTX_RES_SHIFT 0x18U
#define RG_CDTX_L0P_HSTX_RES_MASK 0x1F000000U
#define RG_CDTX_L1N_HSTX_RES_WIDTH 0x5U
#define RG_CDTX_L1N_HSTX_RES_SHIFT 0x0U
#define RG_CDTX_L1N_HSTX_RES_MASK 0x1FU
#define RG_CDTX_L1P_HSTX_RES_WIDTH 0x5U
#define RG_CDTX_L1P_HSTX_RES_SHIFT 0x5U
#define RG_CDTX_L1P_HSTX_RES_MASK 0x3E0U
#define RG_CDTX_L2N_HSTX_RES_WIDTH 0x5U
#define RG_CDTX_L2N_HSTX_RES_SHIFT 0xAU
#define RG_CDTX_L2N_HSTX_RES_MASK 0x7C00U
#define RG_CDTX_L2P_HSTX_RES_WIDTH 0x5U
#define RG_CDTX_L2P_HSTX_RES_SHIFT 0xFU
#define RG_CDTX_L2P_HSTX_RES_MASK 0xF8000U
#define RG_CDTX_L3N_HSTX_RES_WIDTH 0x5U
#define RG_CDTX_L3N_HSTX_RES_SHIFT 0x14U
#define RG_CDTX_L3N_HSTX_RES_MASK 0x1F00000U
#define RG_CDTX_L3P_HSTX_RES_WIDTH 0x5U
#define RG_CDTX_L3P_HSTX_RES_SHIFT 0x19U
#define RG_CDTX_L3P_HSTX_RES_MASK 0x3E000000U
#define RG_CDTX_L4N_HSTX_RES_WIDTH 0x5U
#define RG_CDTX_L4N_HSTX_RES_SHIFT 0x0U
#define RG_CDTX_L4N_HSTX_RES_MASK 0x1FU
#define RG_CDTX_L4P_HSTX_RES_WIDTH 0x5U
#define RG_CDTX_L4P_HSTX_RES_SHIFT 0x5U
#define RG_CDTX_L4P_HSTX_RES_MASK 0x3E0U
#define RG_CDTX_PLL_FBK_FRA_WIDTH 0x18U
#define RG_CDTX_PLL_FBK_FRA_SHIFT 0x0U
#define RG_CDTX_PLL_FBK_FRA_MASK 0xFFFFFFU
#define RG_CDTX_PLL_FBK_INT_WIDTH 0x9U
#define RG_CDTX_PLL_FBK_INT_SHIFT 0x0U
#define RG_CDTX_PLL_FBK_INT_MASK 0x1FFU
#define RG_CDTX_PLL_FM_EN_WIDTH 0x1U
#define RG_CDTX_PLL_FM_EN_SHIFT 0x9U
#define RG_CDTX_PLL_FM_EN_MASK 0x200U
#define RG_CDTX_PLL_LDO_STB_X2_EN_WIDTH 0x1U
#define RG_CDTX_PLL_LDO_STB_X2_EN_SHIFT 0xAU
#define RG_CDTX_PLL_LDO_STB_X2_EN_MASK 0x400U
#define RG_CDTX_PLL_PRE_DIV_WIDTH 0x2U
#define RG_CDTX_PLL_PRE_DIV_SHIFT 0xBU
#define RG_CDTX_PLL_PRE_DIV_MASK 0x1800U
#define RG_CDTX_PLL_SSC_DELTA_WIDTH 0x12U
#define RG_CDTX_PLL_SSC_DELTA_SHIFT 0xDU
#define RG_CDTX_PLL_SSC_DELTA_MASK 0x7FFFE000U
#define RG_CDTX_PLL_SSC_DELTA_INIT_WIDTH 0x12U
#define RG_CDTX_PLL_SSC_DELTA_INIT_SHIFT 0x0U
#define RG_CDTX_PLL_SSC_DELTA_INIT_MASK 0x3FFFFU
#define RG_CDTX_PLL_SSC_EN_WIDTH 0x1U
#define RG_CDTX_PLL_SSC_EN_SHIFT 0x12U
#define RG_CDTX_PLL_SSC_EN_MASK 0x40000U
#define RG_CDTX_PLL_SSC_PRD_WIDTH 0xAU
#define RG_CDTX_PLL_SSC_PRD_SHIFT 0x13U
#define RG_CDTX_PLL_SSC_PRD_MASK 0x1FF80000U
#define RG_CLANE_HS_CLK_POST_TIME_WIDTH 0x8U
#define RG_CLANE_HS_CLK_POST_TIME_SHIFT 0x0U
#define RG_CLANE_HS_CLK_POST_TIME_MASK 0xFFU
#define RG_CLANE_HS_CLK_PRE_TIME_WIDTH 0x8U
#define RG_CLANE_HS_CLK_PRE_TIME_SHIFT 0x8U
#define RG_CLANE_HS_CLK_PRE_TIME_MASK 0xFF00U
#define RG_CLANE_HS_PRE_TIME_WIDTH 0x8U
#define RG_CLANE_HS_PRE_TIME_SHIFT 0x10U
#define RG_CLANE_HS_PRE_TIME_MASK 0xFF0000U
#define RG_CLANE_HS_TRAIL_TIME_WIDTH 0x8U
#define RG_CLANE_HS_TRAIL_TIME_SHIFT 0x18U
#define RG_CLANE_HS_TRAIL_TIME_MASK 0xFF000000U
#define RG_CLANE_HS_ZERO_TIME_WIDTH 0x8U
#define RG_CLANE_HS_ZERO_TIME_SHIFT 0x0U
#define RG_CLANE_HS_ZERO_TIME_MASK 0xFFU
#define RG_DLANE_HS_PRE_TIME_WIDTH 0x8U
#define RG_DLANE_HS_PRE_TIME_SHIFT 0x8U
#define RG_DLANE_HS_PRE_TIME_MASK 0xFF00U
#define RG_DLANE_HS_TRAIL_TIME_WIDTH 0x8U
#define RG_DLANE_HS_TRAIL_TIME_SHIFT 0x10U
#define RG_DLANE_HS_TRAIL_TIME_MASK 0xFF0000U
#define RG_DLANE_HS_ZERO_TIME_WIDTH 0x8U
#define RG_DLANE_HS_ZERO_TIME_SHIFT 0x18U
#define RG_DLANE_HS_ZERO_TIME_MASK 0xFF000000U
#define RG_EXTD_CYCLE_SEL_WIDTH 0x3U
#define RG_EXTD_CYCLE_SEL_SHIFT 0x0U
#define RG_EXTD_CYCLE_SEL_MASK 0x7U
#define SCFG_C_HS_PRE_ZERO_TIME_WIDTH 0x20U
#define SCFG_C_HS_PRE_ZERO_TIME_SHIFT 0x0U
#define SCFG_C_HS_PRE_ZERO_TIME_MASK 0xFFFFFFFFU
#define SCFG_DPHY_SRC_SEL_WIDTH 0x1U
#define SCFG_DPHY_SRC_SEL_SHIFT 0x0U
#define SCFG_DPHY_SRC_SEL_MASK 0x1U
#define SCFG_DSI_TXREADY_ESC_SEL_WIDTH 0x2U
#define SCFG_DSI_TXREADY_ESC_SEL_SHIFT 0x1U
#define SCFG_DSI_TXREADY_ESC_SEL_MASK 0x6U
#define SCFG_PPI_C_READY_SEL_WIDTH 0x2U
#define SCFG_PPI_C_READY_SEL_SHIFT 0x3U
#define SCFG_PPI_C_READY_SEL_MASK 0x18U
#define VCONTROL_WIDTH 0x5U
#define VCONTROL_SHIFT 0x5U
#define VCONTROL_MASK 0x3E0U
#define XCFGI_DW00_WIDTH 0x20U
#define XCFGI_DW00_SHIFT 0x0U
#define XCFGI_DW00_MASK 0xFFFFFFFFU
#define XCFGI_DW01_WIDTH 0x20U
#define XCFGI_DW01_SHIFT 0x0U
#define XCFGI_DW01_MASK 0xFFFFFFFFU
#define XCFGI_DW02_WIDTH 0x20U
#define XCFGI_DW02_SHIFT 0x0U
#define XCFGI_DW02_MASK 0xFFFFFFFFU
#define XCFGI_DW03_WIDTH 0x20U
#define XCFGI_DW03_SHIFT 0x0U
#define XCFGI_DW03_MASK 0xFFFFFFFFU
#define XCFGI_DW04_WIDTH 0x20U
#define XCFGI_DW04_SHIFT 0x0U
#define XCFGI_DW04_MASK 0xFFFFFFFFU
#define XCFGI_DW05_WIDTH 0x20U
#define XCFGI_DW05_SHIFT 0x0U
#define XCFGI_DW05_MASK 0xFFFFFFFFU
#define XCFGI_DW06_WIDTH 0x20U
#define XCFGI_DW06_SHIFT 0x0U
#define XCFGI_DW06_MASK 0xFFFFFFFFU
#define XCFGI_DW07_WIDTH 0x20U
#define XCFGI_DW07_SHIFT 0x0U
#define XCFGI_DW07_MASK 0xFFFFFFFFU
#define XCFGI_DW08_WIDTH 0x20U
#define XCFGI_DW08_SHIFT 0x0U
#define XCFGI_DW08_MASK 0xFFFFFFFFU
#define XCFGI_DW09_WIDTH 0x20U
#define XCFGI_DW09_SHIFT 0x0U
#define XCFGI_DW09_MASK 0xFFFFFFFFU
#define XCFGI_DW0A_WIDTH 0x20U
#define XCFGI_DW0A_SHIFT 0x0U
#define XCFGI_DW0A_MASK 0xFFFFFFFFU
#define XCFGI_DW0B_WIDTH 0x20U
#define XCFGI_DW0B_SHIFT 0x0U
#define XCFGI_DW0B_MASK 0xFFFFFFFFU
#define DBG1_MUX_DOUT_WIDTH 0x8U
#define DBG1_MUX_DOUT_SHIFT 0x0U
#define DBG1_MUX_DOUT_MASK 0xFFU
#define DBG1_MUX_SEL_WIDTH 0x5U
#define DBG1_MUX_SEL_SHIFT 0x8U
#define DBG1_MUX_SEL_MASK 0x1F00U
#define DBG2_MUX_DOUT_WIDTH 0x8U
#define DBG2_MUX_DOUT_SHIFT 0xDU
#define DBG2_MUX_DOUT_MASK 0x1FE000U
#define DBG2_MUX_SEL_WIDTH 0x5U
#define DBG2_MUX_SEL_SHIFT 0x15U
#define DBG2_MUX_SEL_MASK 0x3E00000U
#define REFCLK_IN_SEL_WIDTH 0x3U
#define REFCLK_IN_SEL_SHIFT 0x1AU
#define REFCLK_IN_SEL_MASK 0x1C000000U
#define RESETB_WIDTH 0x1U
#define RESETB_SHIFT 0x1DU
#define RESETB_MASK 0x20000000U
//aonsys con
#define AON_GP_REG_WIDTH 0x20U
#define AON_GP_REG_SHIFT 0x0U
#define AON_GP_REG_MASK 0xFFFFFFFFU
#define M31_DPHY_REFCLK_RESERVED 0
#define M31_DPHY_REFCLK_12M 1
#define M31_DPHY_REFCLK_19_2M 2
#define M31_DPHY_REFCLK_25M 3
#define M31_DPHY_REFCLK_26M 4
#define M31_DPHY_REFCLK_27M 5
#define M31_DPHY_REFCLK_38_4M 6
#define M31_DPHY_REFCLK_52M 7
#define M31_DPHY_REFCLK_BUTT 8
#define DPHY_TX_PSW_EN_MASK (1<<30)
struct m31_dphy_config {
int ref_clk;
unsigned long bitrate;
uint32_t pll_prev_div, pll_fbk_int, pll_fbk_fra, extd_cycle_sel;
uint32_t dlane_hs_pre_time, dlane_hs_zero_time, dlane_hs_trail_time;
uint32_t clane_hs_pre_time, clane_hs_zero_time, clane_hs_trail_time;
uint32_t clane_hs_clk_pre_time, clane_hs_clk_post_time;
};
#define M31_DPHY_REFCLK M31_DPHY_REFCLK_12M
#define M31_DPHY_BITRATE_ALIGN 10000000
static const struct m31_dphy_config m31_dphy_configs[] = {
#if (M31_DPHY_REFCLK == M31_DPHY_REFCLK_25M)
{25000000, 100000000, 0x1, 0x80, 0x000000, 0x4, 0x10, 0x21, 0x17, 0x07, 0x35, 0x0F, 0x0F, 0x73,},
{25000000, 200000000, 0x1, 0x80, 0x000000, 0x3, 0x0C, 0x1B, 0x13, 0x07, 0x35, 0x0F, 0x07, 0x3F,},
{25000000, 300000000, 0x1, 0xC0, 0x000000, 0x3, 0x11, 0x25, 0x19, 0x0A, 0x50, 0x15, 0x07, 0x45,},
{25000000, 400000000, 0x1, 0x80, 0x000000, 0x2, 0x0A, 0x18, 0x11, 0x07, 0x35, 0x0F, 0x03, 0x25,},
{25000000, 500000000, 0x1, 0xA0, 0x000000, 0x2, 0x0C, 0x1D, 0x14, 0x09, 0x42, 0x12, 0x03, 0x28,},
{25000000, 600000000, 0x1, 0xC0, 0x000000, 0x2, 0x0E, 0x23, 0x17, 0x0A, 0x50, 0x15, 0x03, 0x2B,},
{25000000, 700000000, 0x1, 0x70, 0x000000, 0x1, 0x08, 0x14, 0x0F, 0x06, 0x2F, 0x0E, 0x01, 0x16,},
{25000000, 800000000, 0x1, 0x80, 0x000000, 0x1, 0x09, 0x17, 0x10, 0x07, 0x35, 0x0F, 0x01, 0x18,},
{25000000, 900000000, 0x1, 0x90, 0x000000, 0x1, 0x0A, 0x19, 0x12, 0x08, 0x3C, 0x10, 0x01, 0x19,},
{25000000, 1000000000, 0x1, 0xA0, 0x000000, 0x1, 0x0B, 0x1C, 0x13, 0x09, 0x42, 0x12, 0x01, 0x1B,},
{25000000, 1100000000, 0x1, 0xB0, 0x000000, 0x1, 0x0C, 0x1E, 0x15, 0x09, 0x4A, 0x14, 0x01, 0x1D,},
{25000000, 1200000000, 0x1, 0xC0, 0x000000, 0x1, 0x0E, 0x20, 0x16, 0x0A, 0x50, 0x15, 0x01, 0x1E,},
{25000000, 1300000000, 0x1, 0x68, 0x000000, 0x0, 0x07, 0x12, 0x0D, 0x05, 0x2C, 0x0D, 0x00, 0x0F,},
{25000000, 1400000000, 0x1, 0x70, 0x000000, 0x0, 0x07, 0x14, 0x0E, 0x06, 0x2F, 0x0E, 0x00, 0x10,},
{25000000, 1500000000, 0x1, 0x78, 0x000000, 0x0, 0x08, 0x14, 0x0F, 0x06, 0x32, 0x0E, 0x00, 0x11,},
{25000000, 1600000000, 0x1, 0x80, 0x000000, 0x0, 0x09, 0x15, 0x10, 0x07, 0x35, 0x0F, 0x00, 0x12,},
{25000000, 1700000000, 0x1, 0x88, 0x000000, 0x0, 0x09, 0x17, 0x10, 0x07, 0x39, 0x10, 0x00, 0x12,},
{25000000, 1800000000, 0x1, 0x90, 0x000000, 0x0, 0x0A, 0x18, 0x11, 0x08, 0x3C, 0x10, 0x00, 0x13,},
{25000000, 1900000000, 0x1, 0x98, 0x000000, 0x0, 0x0A, 0x1A, 0x12, 0x08, 0x3F, 0x11, 0x00, 0x14,},
{25000000, 2000000000, 0x1, 0xA0, 0x000000, 0x0, 0x0B, 0x1B, 0x13, 0x09, 0x42, 0x12, 0x00, 0x15,},
{25000000, 2100000000, 0x1, 0xA8, 0x000000, 0x0, 0x0B, 0x1C, 0x13, 0x09, 0x46, 0x13, 0x00, 0x15,},
{25000000, 2200000000, 0x1, 0xB0, 0x000000, 0x0, 0x0C, 0x1D, 0x14, 0x09, 0x4A, 0x14, 0x00, 0x16,},
{25000000, 2300000000, 0x1, 0xB8, 0x000000, 0x0, 0x0C, 0x1F, 0x15, 0x0A, 0x4C, 0x14, 0x00, 0x17,},
{25000000, 2400000000, 0x1, 0xC0, 0x000000, 0x0, 0x0D, 0x20, 0x16, 0x0A, 0x50, 0x15, 0x00, 0x18,},
{25000000, 2500000000, 0x1, 0xC8, 0x000000, 0x0, 0x0E, 0x21, 0x16, 0x0B, 0x53, 0x16, 0x00, 0x18,},
#elif (M31_DPHY_REFCLK == M31_DPHY_REFCLK_12M)
{12000000, 500000000, 0x0, 0xa6, 0xaa<<16|0xaa<<8|0xaa, 0x2, 0xc, 0x1d, 0x14, 0x9, 0x42, 0x12, 0x3, 0x28,},
{12000000, 510000000, 0x0, 0xaa, 0x0<<16|0x0<<8|0x0, 0x2, 0xc, 0x1e, 0x14, 0x9, 0x44, 0x12, 0x3, 0x28,},
{12000000, 520000000, 0x0, 0xad, 0x55<<16|0x55<<8|0x55, 0x2, 0xd, 0x1e, 0x15, 0x9, 0x45, 0x13, 0x3, 0x29,},
{12000000, 530000000, 0x0, 0xb0, 0xaa<<16|0xaa<<8|0xaa, 0x2, 0xd, 0x1e, 0x15, 0x9, 0x47, 0x13, 0x3, 0x29,},
{12000000, 540000000, 0x0, 0xb4, 0x0<<16|0x0<<8|0x0, 0x2, 0xd, 0x1f, 0x15, 0x9, 0x48, 0x13, 0x3, 0x29,},
{12000000, 550000000, 0x0, 0xb7, 0x55<<16|0x55<<8|0x55, 0x2, 0xd, 0x20, 0x16, 0x9, 0x4a, 0x14, 0x3, 0x2a,},
{12000000, 560000000, 0x0, 0xba, 0xaa<<16|0xaa<<8|0xaa, 0x2, 0xe, 0x20, 0x16, 0xa, 0x4a, 0x14, 0x3, 0x2a,},
{12000000, 570000000, 0x0, 0xbe, 0x0<<16|0x0<<8|0x0, 0x2, 0xe, 0x20, 0x16, 0xa, 0x4c, 0x14, 0x3, 0x2a,},
{12000000, 580000000, 0x0, 0xc1, 0x55<<16|0x55<<8|0x55, 0x2, 0xe, 0x21, 0x16, 0xa, 0x4d, 0x14, 0x3, 0x2a,},
{12000000, 590000000, 0x0, 0xc4, 0xaa<<16|0xaa<<8|0xaa, 0x2, 0xe, 0x22, 0x17, 0xa, 0x4f, 0x15, 0x3, 0x2b,},
{12000000, 600000000, 0x0, 0xc8, 0x0<<16|0x0<<8|0x0, 0x2, 0xe, 0x23, 0x17, 0xa, 0x50, 0x15, 0x3, 0x2b,},
{12000000, 610000000, 0x0, 0xcb, 0x55<<16|0x55<<8|0x55, 0x2, 0xf, 0x22, 0x17, 0xb, 0x50, 0x15, 0x3, 0x2b,},
{12000000, 620000000, 0x0, 0xce, 0xaa<<16|0xaa<<8|0xaa, 0x2, 0xf, 0x23, 0x18, 0xb, 0x52, 0x16, 0x3, 0x2c,},
{12000000, 630000000, 0x0, 0x69, 0x0<<16|0x0<<8|0x0, 0x1, 0x7, 0x12, 0xd, 0x5, 0x2a, 0xc, 0x1, 0x15,},
{12000000, 640000000, 0x0, 0x6a, 0xaa<<16|0xaa<<8|0xaa, 0x1, 0x7, 0x13, 0xe, 0x5, 0x2b, 0xd, 0x1, 0x16,},
{12000000, 650000000, 0x0, 0x6c, 0x55<<16|0x55<<8|0x55, 0x1, 0x7, 0x13, 0xe, 0x5, 0x2c, 0xd, 0x1, 0x16,},
{12000000, 660000000, 0x0, 0x6e, 0x0<<16|0x0<<8|0x0, 0x1, 0x7, 0x13, 0xe, 0x5, 0x2d, 0xd, 0x1, 0x16,},
{12000000, 670000000, 0x0, 0x6f, 0xaa<<16|0xaa<<8|0xaa, 0x1, 0x8, 0x13, 0xe, 0x5, 0x2d, 0xd, 0x1, 0x16,},
{12000000, 680000000, 0x0, 0x71, 0x55<<16|0x55<<8|0x55, 0x1, 0x8, 0x13, 0xe, 0x5, 0x2e, 0xd, 0x1, 0x16,},
{12000000, 690000000, 0x0, 0x73, 0x0<<16|0x0<<8|0x0, 0x1, 0x8, 0x14, 0xe, 0x6, 0x2e, 0xd, 0x1, 0x16,},
{12000000, 700000000, 0x0, 0x74, 0xaa<<16|0xaa<<8|0xaa, 0x1, 0x8, 0x14, 0xf, 0x6, 0x2f, 0xe, 0x1, 0x16,},
{12000000, 710000000, 0x0, 0x76, 0x55<<16|0x55<<8|0x55, 0x1, 0x8, 0x14, 0xf, 0x6, 0x2f, 0xe, 0x1, 0x17,},
{12000000, 720000000, 0x0, 0x78, 0x0<<16|0x0<<8|0x0, 0x1, 0x8, 0x15, 0xf, 0x6, 0x30, 0xe, 0x1, 0x17,},
{12000000, 730000000, 0x0, 0x79, 0xaa<<16|0xaa<<8|0xaa, 0x1, 0x8, 0x15, 0xf, 0x6, 0x31, 0xe, 0x1, 0x17,},
{12000000, 740000000, 0x0, 0x7b, 0x55<<16|0x55<<8|0x55, 0x1, 0x8, 0x15, 0xf, 0x6, 0x32, 0xe, 0x1, 0x17,},
{12000000, 750000000, 0x0, 0x7d, 0x0<<16|0x0<<8|0x0, 0x1, 0x8, 0x16, 0xf, 0x6, 0x32, 0xe, 0x1, 0x17,},
{12000000, 760000000, 0x0, 0x7e, 0xaa<<16|0xaa<<8|0xaa, 0x1, 0x9, 0x15, 0xf, 0x6, 0x33, 0xe, 0x1, 0x17,},
{12000000, 770000000, 0x0, 0x80, 0x55<<16|0x55<<8|0x55, 0x1, 0x9, 0x15, 0x10, 0x6, 0x34, 0xf, 0x1, 0x18,},
{12000000, 780000000, 0x0, 0x82, 0x0<<16|0x0<<8|0x0, 0x1, 0x9, 0x16, 0x10, 0x6, 0x35, 0xf, 0x1, 0x18,},
{12000000, 790000000, 0x0, 0x83, 0xaa<<16|0xaa<<8|0xaa, 0x1, 0x9, 0x16, 0x10, 0x7, 0x34, 0xf, 0x1, 0x18,},
{12000000, 800000000, 0x0, 0x85, 0x55<<16|0x55<<8|0x55, 0x1, 0x9, 0x17, 0x10, 0x7, 0x35, 0xf, 0x1, 0x18,},
{12000000, 810000000, 0x0, 0x87, 0x0<<16|0x0<<8|0x0, 0x1, 0x9, 0x17, 0x10, 0x7, 0x36, 0xf, 0x1, 0x18,},
{12000000, 820000000, 0x0, 0x88, 0xaa<<16|0xaa<<8|0xaa, 0x1, 0x9, 0x17, 0x10, 0x7, 0x37, 0xf, 0x1, 0x18,},
{12000000, 830000000, 0x0, 0x8a, 0x55<<16|0x55<<8|0x55, 0x1, 0x9, 0x18, 0x10, 0x7, 0x37, 0xf, 0x1, 0x18,},
{12000000, 840000000, 0x0, 0x8c, 0x0<<16|0x0<<8|0x0, 0x1, 0x9, 0x18, 0x11, 0x7, 0x38, 0x10, 0x1, 0x19,},
{12000000, 850000000, 0x0, 0x8d, 0xaa<<16|0xaa<<8|0xaa, 0x1, 0xa, 0x17, 0x11, 0x7, 0x39, 0x10, 0x1, 0x19,},
{12000000, 860000000, 0x0, 0x8f, 0x55<<16|0x55<<8|0x55, 0x1, 0xa, 0x18, 0x11, 0x7, 0x39, 0x10, 0x1, 0x19,},
{12000000, 870000000, 0x0, 0x91, 0x0<<16|0x0<<8|0x0, 0x1, 0xa, 0x18, 0x11, 0x7, 0x3a, 0x10, 0x1, 0x19,},
{12000000, 880000000, 0x0, 0x92, 0xaa<<16|0xaa<<8|0xaa, 0x1, 0xa, 0x18, 0x11, 0x7, 0x3b, 0x10, 0x1, 0x19,},
{12000000, 890000000, 0x0, 0x94, 0x55<<16|0x55<<8|0x55, 0x1, 0xa, 0x19, 0x11, 0x7, 0x3c, 0x10, 0x1, 0x19,},
{12000000, 900000000, 0x0, 0x96, 0x0<<16|0x0<<8|0x0, 0x1, 0xa, 0x19, 0x12, 0x8, 0x3c, 0x10, 0x1, 0x19,},
{12000000, 910000000, 0x0, 0x97, 0xaa<<16|0xaa<<8|0xaa, 0x1, 0xa, 0x19, 0x12, 0x8, 0x3c, 0x11, 0x1, 0x1a,},
{12000000, 920000000, 0x0, 0x99, 0x55<<16|0x55<<8|0x55, 0x1, 0xa, 0x1a, 0x12, 0x8, 0x3d, 0x11, 0x1, 0x1a,},
{12000000, 930000000, 0x0, 0x9b, 0x0<<16|0x0<<8|0x0, 0x1, 0xa, 0x1a, 0x12, 0x8, 0x3e, 0x11, 0x1, 0x1a,},
{12000000, 940000000, 0x0, 0x9c, 0xaa<<16|0xaa<<8|0xaa, 0x1, 0xb, 0x1a, 0x12, 0x8, 0x3e, 0x11, 0x1, 0x1a,},
{12000000, 950000000, 0x0, 0x9e, 0x55<<16|0x55<<8|0x55, 0x1, 0xb, 0x1a, 0x12, 0x8, 0x3f, 0x11, 0x1, 0x1a,},
{12000000, 960000000, 0x0, 0xa0, 0x0<<16|0x0<<8|0x0, 0x1, 0xb, 0x1a, 0x12, 0x8, 0x40, 0x11, 0x1, 0x1a,},
{12000000, 970000000, 0x0, 0xa1, 0xaa<<16|0xaa<<8|0xaa, 0x1, 0xb, 0x1b, 0x13, 0x8, 0x41, 0x12, 0x1, 0x1b,},
{12000000, 980000000, 0x0, 0xa3, 0x55<<16|0x55<<8|0x55, 0x1, 0xb, 0x1b, 0x13, 0x8, 0x42, 0x12, 0x1, 0x1b,},
{12000000, 990000000, 0x0, 0xa5, 0x0<<16|0x0<<8|0x0, 0x1, 0xb, 0x1b, 0x13, 0x8, 0x42, 0x12, 0x1, 0x1b,},
{12000000, 1000000000, 0x0, 0xa6, 0xaa<<16|0xaa<<8|0xaa, 0x1, 0xb, 0x1c, 0x13, 0x9, 0x42, 0x12, 0x1, 0x1b,},
#endif
};
static inline u32 sf_dphy_get_reg(void* io_addr, u32 shift, u32 mask)
{
//void __iomem *io_addr = ioremap(addr, 0x10000);
u32 tmp;
tmp = readl(io_addr);
tmp = (tmp & mask) >> shift;
return tmp;
}
static inline void sf_dphy_set_reg(void* io_addr, u32 data, u32 shift, u32 mask)
{
//void __iomem *io_addr = ioremap(addr, 0x10000);
u32 tmp;
tmp = readl(io_addr);
tmp &= ~mask;
tmp |= (data << shift) & mask;
writel(tmp, io_addr);
}
static inline void sf_dphy_assert_rst(void* io_addr, u32 addr_status, u32 mask)
{
//void __iomem *io_addr = ioremap(addr, 0x4);
void __iomem *io_addr_status = ioremap(addr_status, 0x4);
u32 tmp;
tmp = readl(io_addr);
tmp |= mask;
writel(tmp,io_addr);
do{
tmp = readl(io_addr_status);
}while((tmp & mask)!=0);
}
static inline void sf_dphy_clear_rst (void* io_addr, u32 addr_status, u32 mask)
{
//void __iomem *io_addr = ioremap(addr, 0x4);
void __iomem *io_addr_status = ioremap(addr_status, 0x4);
u32 tmp;
tmp = readl(io_addr);
tmp &= ~mask;
writel(tmp, io_addr);
do{
tmp = readl(io_addr_status);
}while((tmp & mask) != mask);
}
#endif /* __7110_M31_DPHY_H__ */

15
drivers/phy/m31/Kconfig Normal file
View file

@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Phy drivers for Starfive platforms
#
config PHY_M31_DPHY_RX0
tristate "Starfive M31 MIPI DPHY TX Driver"
select GENERIC_PHY_MIPI_DPHY
select GENERIC_PHY
help
Enable this to support the starfive MIPI DPHY TX0
To compile this driver as a module, choose M here: the module
will be called phy-starfive-dphy-tx0.

3
drivers/phy/m31/Makefile Normal file
View file

@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_PHY_M31_DPHY_RX0) += phy-m31-dphy-tx0.o

View file

@ -0,0 +1,789 @@
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Rockchip MIPI Synopsys DPHY RX0 driver
*
* Copyright (C) 2019 Collabora, Ltd.
*
* Based on:
*
* drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
* in https://chromium.googlesource.com/chromiumos/third_party/kernel,
* chromeos-4.4 branch.
*
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
* Jacob Chen <jacob2.chen@rock-chips.com>
* Shunqian Zheng <zhengsq@rock-chips.com>
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/phy/phy-mipi-dphy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include<linux/reset.h>
#include <linux/regulator/consumer.h>
#include "7110-m31-dphy.h"
//syscfg registers
#define SCFG_DSI_CSI_SEL 0x2c
#define SCFG_PHY_RESETB 0x30
#define SCFG_REFCLK_SEL 0x34
#define SCFG_DBUS_PW_PLL_SSC_LD0 0x38
#define SCFG_GRS_CDTX_PLL 0x3c
#define SCFG_RG_CDTX_PLL_FBK_PRE 0x44
#define SCFG_RG_CLANE_DLANE_TIME 0x58
#define SCFG_RG_CLANE_HS_TIME 0x58
#define SCFG_RG_EXTD_CYCLE_SEL 0x5c
#define SCFG_L0N_L0P_HSTX 0x60
#define SCFG_L1N_L1P_HSTX 0x64
#define SCFG_L2N_L2P_HSTX 0x68
#define SCFG_L3N_L3P_HSTX 0x6c
#define SCFG_L4N_L4P_HSTX 0x70
#define SCFG_LX_SWAP_SEL 0x78
#define SCFG_HS_PRE_ZERO_T_D 0xc4
#define SCFG_TXREADY_SRC_SEL_D 0xc8
#define SCFG_HS_PRE_ZERO_T_C 0xd4
#define SCFG_TXREADY_SRC_SEL_C 0xd8
//reg SCFG_LX_SWAP_SEL
#define OFFSET_CFG_L0_SWAP_SEL 0
#define OFFSET_CFG_L1_SWAP_SEL 3
#define OFFSET_CFG_L2_SWAP_SEL 6
#define OFFSET_CFG_L3_SWAP_SEL 9
#define OFFSET_CFG_L4_SWAP_SEL 12
//reg SCFG_DBUS_PW_PLL_SSC_LD0
#define OFFSET_SCFG_CFG_DATABUD16_SEL 0
#define OFFSET_SCFG_PWRON_READY_N 1
#define OFFSET_RG_CDTX_PLL_FM_EN 2
#define OFFSET_SCFG_PLLSSC_EN 3
#define OFFSET_RG_CDTX_PLL_LDO_STB_X2_EN 4
//reg SCFG_RG_CLANE_DLANE_TIME
#define OFFSET_DHS_PRE_TIME 8
#define OFFSET_DHS_TRIAL_TIME 16
#define OFFSET_DHS_ZERO_TIME 24
//reg SCFG_RG_CLANE_HS_TIME
#define OFFSET_CHS_PRE_TIME 8
#define OFFSET_CHS_TRIAL_TIME 16
#define OFFSET_CHS_ZERO_TIME 24
//dsitx registers
#define VID_MCTL_MAIN_DATA_CTL 0x04
#define VID_MCTL_MAIN_PHY_CTL 0x08
#define VID_MCTL_MAIN_EN 0x0c
#define VID_MAIN_CTRL_ADDR 0xb0
#define VID_VSIZE1_ADDR 0xb4
#define VID_VSIZE2_ADDR 0xb8
#define VID_HSIZE1_ADDR 0xc0
#define VID_HSIZE2_ADDR 0xc4
#define VID_BLKSIZE1_ADDR 0xCC
#define VID_BLKSIZE2_ADDR 0xd0
#define VID_PCK_TIME_ADDR 0xd8
#define VID_DPHY_TIME_ADDR 0xdc
#define VID_ERR_COLOR1_ADDR 0xe0
#define VID_ERR_COLOR2_ADDR 0xe4
#define VID_VPOS_ADDR 0xe8
#define VID_HPOS_ADDR 0xec
#define VID_MODE_STAT_ADDR 0xf0
#define VID_VCA_SET1_ADDR 0xf4
#define VID_VCA_SET2_ADDR 0xf8
#define VID_MODE_STAT_CLR_ADDR 0x160
#define VID_MODE_STAT_FLAG_ADDR 0x180
#define TVG_CTRL_ADDR 0x0fc
#define TVG_IMG_SIZE_ADDR 0x100
#define TVG_COLOR1_ADDR 0x104
#define TVG_COLOR1BIT_ADDR 0x108
#define TVG_COLOR2_ADDR 0x10c
#define TVG_COLOR2BIT_ADDR 0x110
#define TVG_STAT_ADDR 0x114
#define TVG_STAT_CTRL_ADDR 0x144
#define TVG_STAT_CLR_ADDR 0x164
#define TVG_STAT_FLAG_ADDR 0x184
#define DPI_IRQ_EN_ADDR 0x1a0
#define DPI_IRQ_CLR_ADDR 0x1a4
#define DPI_IRQ_STAT_ADDR 0x1a4
#define DPI_CFG_ADDR 0x1ac
//sysrst registers
#define SRST_ASSERT0 0x00
#define SRST_STATUS0 0x04
/* Definition controller bit for syd rst registers */
#define BIT_RST_DSI_DPI_PIX 17
struct sf_dphy {
struct device *dev;
void __iomem *topsys;
struct clk_bulk_data *clks;
struct clk * txesc_clk;
struct reset_control *sys_rst;
struct reset_control *txbytehs_rst;
void __iomem *aonsys;//7110 aonsys con
struct phy_configure_opts_mipi_dphy config;
u8 hsfreq;
struct regulator *mipitx_1p8;
struct regulator *mipitx_0p9;
struct phy *phy;
};
static int sf_dphy_clkrst_get(struct device *dev, struct sf_dphy *dphy)
{
dphy->txesc_clk = devm_clk_get(dev, "dphy_txesc");
if (IS_ERR(dphy->txesc_clk)){
dev_err(dev, "===txesc_clk get error\n");
return PTR_ERR(dphy->txesc_clk);
}
dphy->sys_rst = reset_control_get_exclusive(dev, "dphy_sys");
if (IS_ERR(dphy->sys_rst)){
dev_err(dev, "===sys_rst get error\n");
return PTR_ERR(dphy->sys_rst);
}
return 0;
}
static int sf_dphy_clkrst_ena_deas(struct device *dev, struct sf_dphy *dphy)
{
int ret;
ret = clk_prepare_enable(dphy->txesc_clk);
if (ret) {
dev_err(dev, "failed to prepare/enable txesc_clk\n");
return ret;
}
ret = reset_control_deassert(dphy->sys_rst);
if (ret < 0) {
dev_err(dev, "failed to deassert sys_rst\n");
return ret;
}
return ret;
}
static int sf_dphy_clkrst_disa_assert(struct device *dev, struct sf_dphy *dphy)
{
int ret;
ret = reset_control_assert(dphy->sys_rst);
if (ret < 0) {
dev_err(dev, "failed to assert sys_rst\n");
return ret;
}
clk_disable_unprepare(dphy->txesc_clk);
return ret;
}
/*
*static int sf_dphy_remove(struct platform_device *pdev)
*{
* struct sf_dphy *dphy = dev_get_drvdata(&pdev->dev);
* reset_control_assert(dphy->sys_rst);
* //reset_control_assert(dphy->txbytehs_rst);
* clk_disable_unprepare(dphy->txesc_clk);
* return 0;
*}
*/
#if 0
static u32 top_sys_read32(struct sf_dphy *priv, u32 reg)
{
return ioread32(priv->topsys + reg);
}
static inline void top_sys_write32(struct sf_dphy *priv, u32 reg, u32 val)
{
iowrite32(val, priv->topsys + reg);
}
static void dsi_csi2tx_sel(struct sf_dphy *priv, int sel)
{
u32 temp = 0;
temp = top_sys_read32(priv, SCFG_DSI_CSI_SEL);
temp &= ~(0x1);
temp |= (sel & 0x1);
top_sys_write32(priv, SCFG_DSI_CSI_SEL, temp);
}
static void dphy_clane_hs_txready_sel(struct sf_dphy *priv, u32 ready_sel)
{
top_sys_write32(priv, SCFG_TXREADY_SRC_SEL_D, ready_sel);
top_sys_write32(priv, SCFG_TXREADY_SRC_SEL_C, ready_sel);
top_sys_write32(priv, SCFG_HS_PRE_ZERO_T_D, 0x30);
top_sys_write32(priv, SCFG_HS_PRE_ZERO_T_C, 0x30);
}
static void mipi_tx_lxn_set(struct sf_dphy *priv, u32 reg, u32 n_hstx, u32 p_hstx)
{
u32 temp = 0;
temp = n_hstx;
temp |= p_hstx << 5;
top_sys_write32(priv, reg, temp);
}
static void dphy_config(struct sf_dphy *priv, int bit_rate)
{
int pre_div, fbk_int, extd_cycle_sel;
int dhs_pre_time, dhs_zero_time, dhs_trial_time;
int chs_pre_time, chs_zero_time, chs_trial_time;
int chs_clk_pre_time, chs_clk_post_time;
u32 set_val = 0;
mipi_tx_lxn_set(priv, SCFG_L0N_L0P_HSTX, 0x10, 0x10);
mipi_tx_lxn_set(priv, SCFG_L1N_L1P_HSTX, 0x10, 0x10);
mipi_tx_lxn_set(priv, SCFG_L2N_L2P_HSTX, 0x10, 0x10);
mipi_tx_lxn_set(priv, SCFG_L3N_L3P_HSTX, 0x10, 0x10);
mipi_tx_lxn_set(priv, SCFG_L4N_L4P_HSTX, 0x10, 0x10);
if(bit_rate == 80) {
pre_div=0x1, fbk_int=2*0x33, extd_cycle_sel=0x4,
dhs_pre_time=0xe, dhs_zero_time=0x1d, dhs_trial_time=0x15,
chs_pre_time=0x5, chs_zero_time=0x2b, chs_trial_time=0xd,
chs_clk_pre_time=0xf,
chs_clk_post_time=0x71;
} else if (bit_rate == 100) {
pre_div=0x1, fbk_int=2*0x40, extd_cycle_sel=0x4,
dhs_pre_time=0x10, dhs_zero_time=0x21, dhs_trial_time=0x17,
chs_pre_time=0x7, chs_zero_time=0x35, chs_trial_time=0xf,
chs_clk_pre_time=0xf,
chs_clk_post_time=0x73;
} else if (bit_rate == 200) {
pre_div=0x1, fbk_int=2*0x40, extd_cycle_sel=0x3;
dhs_pre_time=0xc, dhs_zero_time=0x1b, dhs_trial_time=0x13;
chs_pre_time=0x7, chs_zero_time=0x35, chs_trial_time=0xf,
chs_clk_pre_time=0x7,
chs_clk_post_time=0x3f;
} else if(bit_rate == 300) {
pre_div=0x1, fbk_int=2*0x60, extd_cycle_sel=0x3,
dhs_pre_time=0x11, dhs_zero_time=0x25, dhs_trial_time=0x19,
chs_pre_time=0xa, chs_zero_time=0x50, chs_trial_time=0x15,
chs_clk_pre_time=0x7,
chs_clk_post_time=0x45;
} else if(bit_rate == 400) {
pre_div=0x1, fbk_int=2*0x40, extd_cycle_sel=0x2,
dhs_pre_time=0xa, dhs_zero_time=0x18, dhs_trial_time=0x11,
chs_pre_time=0x7, chs_zero_time=0x35, chs_trial_time=0xf,
chs_clk_pre_time=0x3,
chs_clk_post_time=0x25;
} else if(bit_rate == 500 ) {
pre_div=0x1, fbk_int=2*0x50, extd_cycle_sel=0x2,
dhs_pre_time=0xc, dhs_zero_time=0x1d, dhs_trial_time=0x14,
chs_pre_time=0x9, chs_zero_time=0x42, chs_trial_time=0x12,
chs_clk_pre_time=0x3,
chs_clk_post_time=0x28;
} else if(bit_rate == 600 ) {
pre_div=0x1, fbk_int=2*0x60, extd_cycle_sel=0x2,
dhs_pre_time=0xe, dhs_zero_time=0x23, dhs_trial_time=0x17,
chs_pre_time=0xa, chs_zero_time=0x50, chs_trial_time=0x15,
chs_clk_pre_time=0x3,
chs_clk_post_time=0x2b;
} else if(bit_rate == 700) {
pre_div=0x1, fbk_int=2*0x38, extd_cycle_sel=0x1,
dhs_pre_time=0x8, dhs_zero_time=0x14, dhs_trial_time=0xf,
chs_pre_time=0x6, chs_zero_time=0x2f, chs_trial_time=0xe,
chs_clk_pre_time=0x1,
chs_clk_post_time=0x16;
} else if(bit_rate == 800 ) {
pre_div=0x1, fbk_int=2*0x40, extd_cycle_sel=0x1,
dhs_pre_time=0x9, dhs_zero_time=0x17, dhs_trial_time=0x10,
chs_pre_time=0x7, chs_zero_time=0x35, chs_trial_time=0xf,
chs_clk_pre_time=0x1,
chs_clk_post_time=0x18;
} else if(bit_rate == 900 ) {
pre_div=0x1, fbk_int=2*0x48, extd_cycle_sel=0x1,
dhs_pre_time=0xa, dhs_zero_time=0x19, dhs_trial_time=0x12,
chs_pre_time=0x8, chs_zero_time=0x3c, chs_trial_time=0x10,
chs_clk_pre_time=0x1,
chs_clk_post_time=0x19;
} else if(bit_rate == 1000) {
pre_div=0x1, fbk_int=2*0x50, extd_cycle_sel=0x1,
dhs_pre_time=0xb, dhs_zero_time=0x1c, dhs_trial_time=0x13,
chs_pre_time=0x9, chs_zero_time=0x42, chs_trial_time=0x12,
chs_clk_pre_time=0x1,
chs_clk_post_time=0x1b;
} else if(bit_rate == 1100) {
pre_div=0x1, fbk_int=2*0x58, extd_cycle_sel=0x1,
dhs_pre_time=0xc, dhs_zero_time=0x1e, dhs_trial_time=0x15,
chs_pre_time=0x9, chs_zero_time=0x4a, chs_trial_time=0x14,
chs_clk_pre_time=0x1,
chs_clk_post_time=0x1d;
} else if(bit_rate == 1200) {
pre_div=0x1, fbk_int=2*0x60, extd_cycle_sel=0x1,
dhs_pre_time=0xe, dhs_zero_time=0x20, dhs_trial_time=0x16,
chs_pre_time=0xa, chs_zero_time=0x50, chs_trial_time=0x15,
chs_clk_pre_time=0x1,
chs_clk_post_time=0x1e;
} else if(bit_rate == 1300) {
pre_div=0x1, fbk_int=2*0x34, extd_cycle_sel=0x0,
dhs_pre_time=0x7, dhs_zero_time=0x12, dhs_trial_time=0xd,
chs_pre_time=0x5, chs_zero_time=0x2c, chs_trial_time=0xd,
chs_clk_pre_time=0x0,
chs_clk_post_time=0xf;
} else if(bit_rate == 1400) {
pre_div=0x1, fbk_int=2*0x38, extd_cycle_sel=0x0,
dhs_pre_time=0x7, dhs_zero_time=0x14, dhs_trial_time=0xe,
chs_pre_time=0x6, chs_zero_time=0x2f, chs_trial_time=0xe,
chs_clk_pre_time=0x0,
chs_clk_post_time=0x10;
} else if(bit_rate == 1500) {
pre_div=0x1, fbk_int=2*0x3c, extd_cycle_sel=0x0,
dhs_pre_time=0x8, dhs_zero_time=0x14, dhs_trial_time=0xf,
chs_pre_time=0x6, chs_zero_time=0x32, chs_trial_time=0xe,
chs_clk_pre_time=0x0,
chs_clk_post_time=0x11;
} else if(bit_rate == 1600) {
pre_div=0x1, fbk_int=2*0x40, extd_cycle_sel=0x0,
dhs_pre_time=0x9, dhs_zero_time=0x15, dhs_trial_time=0x10,
chs_pre_time=0x7, chs_zero_time=0x35, chs_trial_time=0xf,
chs_clk_pre_time=0x0,
chs_clk_post_time=0x12;
} else if(bit_rate == 1700) {
pre_div=0x1, fbk_int=2*0x44, extd_cycle_sel=0x0,
dhs_pre_time=0x9, dhs_zero_time=0x17, dhs_trial_time=0x10,
chs_pre_time=0x7, chs_zero_time=0x39, chs_trial_time=0x10,
chs_clk_pre_time=0x0,
chs_clk_post_time=0x12;
} else if(bit_rate == 1800) {
pre_div=0x1, fbk_int=2*0x48, extd_cycle_sel=0x0,
dhs_pre_time=0xa, dhs_zero_time=0x18, dhs_trial_time=0x11,
chs_pre_time=0x8, chs_zero_time=0x3c, chs_trial_time=0x10,
chs_clk_pre_time=0x0,
chs_clk_post_time=0x13;
} else if(bit_rate == 1900) {
pre_div=0x1, fbk_int=2*0x4c, extd_cycle_sel=0x0,
dhs_pre_time=0xa, dhs_zero_time=0x1a, dhs_trial_time=0x12,
chs_pre_time=0x8, chs_zero_time=0x3f, chs_trial_time=0x11,
chs_clk_pre_time=0x0,
chs_clk_post_time=0x14;
} else if(bit_rate == 2000) {
pre_div=0x1, fbk_int=2*0x50, extd_cycle_sel=0x0,
dhs_pre_time=0xb, dhs_zero_time=0x1b, dhs_trial_time=0x13,
chs_pre_time=0x9, chs_zero_time=0x42, chs_trial_time=0x12,
chs_clk_pre_time=0x0,
chs_clk_post_time=0x15;
} else if(bit_rate == 2100) {
pre_div=0x1, fbk_int=2*0x54, extd_cycle_sel=0x0,
dhs_pre_time=0xb, dhs_zero_time=0x1c, dhs_trial_time=0x13,
chs_pre_time=0x9, chs_zero_time=0x46, chs_trial_time=0x13,
chs_clk_pre_time=0x0,
chs_clk_post_time=0x15;
} else if(bit_rate == 2200) {
pre_div=0x1, fbk_int=2*0x5b, extd_cycle_sel=0x0,
dhs_pre_time=0xc, dhs_zero_time=0x1d, dhs_trial_time=0x14,
chs_pre_time=0x9, chs_zero_time=0x4a, chs_trial_time=0x14,
chs_clk_pre_time=0x0,
chs_clk_post_time=0x16;
} else if(bit_rate == 2300) {
pre_div=0x1, fbk_int=2*0x5c, extd_cycle_sel=0x0,
dhs_pre_time=0xc, dhs_zero_time=0x1f, dhs_trial_time=0x15,
chs_pre_time=0xa, chs_zero_time=0x4c, chs_trial_time=0x14,
chs_clk_pre_time=0x0,
chs_clk_post_time=0x17;
} else if(bit_rate == 2400) {
pre_div=0x1, fbk_int=2*0x60, extd_cycle_sel=0x0,
dhs_pre_time=0xd, dhs_zero_time=0x20, dhs_trial_time=0x16,
chs_pre_time=0xa, chs_zero_time=0x50, chs_trial_time=0x15,
chs_clk_pre_time=0x0,
chs_clk_post_time=0x18;
} else if(bit_rate == 2500) {
pre_div=0x1, fbk_int=2*0x64, extd_cycle_sel=0x0,
dhs_pre_time=0xe, dhs_zero_time=0x21, dhs_trial_time=0x16,
chs_pre_time=0xb, chs_zero_time=0x53, chs_trial_time=0x16,
chs_clk_pre_time=0x0,
chs_clk_post_time=0x18;
} else {
//default bit_rate == 700
pre_div=0x1, fbk_int=2*0x38, extd_cycle_sel=0x1,
dhs_pre_time=0x8, dhs_zero_time=0x14, dhs_trial_time=0xf,
chs_pre_time=0x6, chs_zero_time=0x2f, chs_trial_time=0xe,
chs_clk_pre_time=0x1,
chs_clk_post_time=0x16;
}
top_sys_write32(priv, SCFG_REFCLK_SEL, 0x3);
set_val = 0
| (1 << OFFSET_CFG_L1_SWAP_SEL)
| (4 << OFFSET_CFG_L2_SWAP_SEL)
| (2 << OFFSET_CFG_L3_SWAP_SEL)
| (3 << OFFSET_CFG_L4_SWAP_SEL);
top_sys_write32(priv, SCFG_LX_SWAP_SEL, set_val);
set_val = 0
| (0 << OFFSET_SCFG_PWRON_READY_N)
| (1 << OFFSET_RG_CDTX_PLL_FM_EN)
| (0 << OFFSET_SCFG_PLLSSC_EN)
| (1 << OFFSET_RG_CDTX_PLL_LDO_STB_X2_EN);
top_sys_write32(priv, SCFG_DBUS_PW_PLL_SSC_LD0, set_val);
set_val = fbk_int
| (pre_div << 9);
top_sys_write32(priv, SCFG_RG_CDTX_PLL_FBK_PRE, set_val);
top_sys_write32(priv, SCFG_RG_EXTD_CYCLE_SEL, extd_cycle_sel);
set_val = chs_zero_time
| (dhs_pre_time << OFFSET_DHS_PRE_TIME)
| (dhs_trial_time << OFFSET_DHS_TRIAL_TIME)
| (dhs_zero_time << OFFSET_DHS_ZERO_TIME);
top_sys_write32(priv, SCFG_RG_CLANE_DLANE_TIME, set_val);
set_val = chs_clk_post_time
| (chs_clk_pre_time << OFFSET_CHS_PRE_TIME)
| (chs_pre_time << OFFSET_CHS_TRIAL_TIME)
| (chs_trial_time << OFFSET_CHS_ZERO_TIME);
top_sys_write32(priv, SCFG_RG_CLANE_HS_TIME, set_val);
}
static void reset_dphy(struct sf_dphy *priv, int resetb)
{
u32 cfg_dsc_enable = 0x01;//bit0
u32 precfg = top_sys_read32(priv, SCFG_PHY_RESETB);
precfg &= ~(cfg_dsc_enable);
precfg |= (resetb&cfg_dsc_enable);
top_sys_write32(priv, SCFG_PHY_RESETB, precfg);
}
static void polling_dphy_lock(struct sf_dphy *priv)
{
int pll_unlock;
udelay(10);
do {
pll_unlock = top_sys_read32(priv, SCFG_GRS_CDTX_PLL) >> 3;
pll_unlock &= 0x1;
} while(pll_unlock == 0x1);
}
static int sf_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
{ //dev_info(dphy->dev,"--->sf_dphy_configure\n");
struct sf_dphy *dphy = phy_get_drvdata(phy);
uint32_t bit_rate = 800000000/1000000UL;//new mipi panel clock setting
//uint32_t bit_rate = 500000000/1000000UL;//7110 mipi panel clock setting
dphy_config(dphy, bit_rate);
reset_dphy(dphy, 1);
mdelay(10);
polling_dphy_lock(dphy);
//dev_info(dphy->dev,"--->sf_dphy_configure\n");
return 0;
}
#endif
static int is_pll_locked(struct sf_dphy *dphy)
{
int tmp = sf_dphy_get_reg(dphy->topsys + 0x8,
RGS_CDTX_PLL_UNLOCK_SHIFT, RGS_CDTX_PLL_UNLOCK_MASK);
return !tmp;
}
static void reset(int assert, struct sf_dphy *dphy)
{
dev_info(dphy->dev, "1 SET_U0_MIPITX_DPHY_RESETB\n");
sf_dphy_set_reg(dphy->topsys + 0x64, (!assert), RESETB_SHIFT, RESETB_MASK);
dev_info(dphy->dev, "2 SET_U0_MIPITX_DPHY_RESETB\n");
if (!assert) {
while(!is_pll_locked(dphy));
dev_info(dphy->dev, "MIPI dphy-tx # PLL Locked\n");
}
}
static int sys_m31_dphy_tx_configure(struct phy *phy, union phy_configure_opts *opts)
{
struct sf_dphy *dphy;
uint32_t bitrate;
unsigned long alignment;
int i;
const struct m31_dphy_config *p;
const uint32_t AON_POWER_READY_N_active = 0;
dphy = phy_get_drvdata(phy);
bitrate = opts->mipi_dphy.hs_clk_rate;//1188M 60fps
dev_info(dphy->dev, "%s bitrate = %ld\n", __func__, bitrate);
sf_dphy_set_reg(dphy->topsys + 0x8, 0x10,
RG_CDTX_L0N_HSTX_RES_SHIFT, RG_CDTX_L0N_HSTX_RES_MASK);
sf_dphy_set_reg(dphy->topsys + 0xC, 0x10,
RG_CDTX_L0N_HSTX_RES_SHIFT, RG_CDTX_L0N_HSTX_RES_MASK);
sf_dphy_set_reg(dphy->topsys + 0xC, 0x10,
RG_CDTX_L2N_HSTX_RES_SHIFT, RG_CDTX_L2N_HSTX_RES_MASK);
sf_dphy_set_reg(dphy->topsys + 0xC, 0x10,
RG_CDTX_L3N_HSTX_RES_SHIFT, RG_CDTX_L3N_HSTX_RES_MASK);
sf_dphy_set_reg(dphy->topsys + 0x10, 0x10,
RG_CDTX_L4N_HSTX_RES_SHIFT, RG_CDTX_L4N_HSTX_RES_MASK);
sf_dphy_set_reg(dphy->topsys + 0x8, 0x10,
RG_CDTX_L0P_HSTX_RES_SHIFT, RG_CDTX_L0P_HSTX_RES_MASK);
sf_dphy_set_reg(dphy->topsys + 0xC, 0x10,
RG_CDTX_L1P_HSTX_RES_SHIFT, RG_CDTX_L1P_HSTX_RES_MASK);
sf_dphy_set_reg(dphy->topsys + 0xC, 0x10,
RG_CDTX_L2P_HSTX_RES_SHIFT, RG_CDTX_L2P_HSTX_RES_MASK);
sf_dphy_set_reg(dphy->topsys + 0xC, 0x10,
RG_CDTX_L3P_HSTX_RES_SHIFT, RG_CDTX_L3P_HSTX_RES_MASK);
sf_dphy_set_reg(dphy->topsys + 0x10, 0x10,
RG_CDTX_L4P_HSTX_RES_SHIFT, RG_CDTX_L4P_HSTX_RES_MASK);
dev_info(dphy->dev,"request dphy hs_rate %dMbps\n", bitrate/1000000);
if (is_pll_locked(dphy))
dev_info(dphy->dev, "Error: MIPI dphy-tx # PLL is not supposed to be LOCKED\n");
else
dev_info(dphy->dev, "MIPI dphy-tx # PLL is not LOCKED\n");
alignment = M31_DPHY_BITRATE_ALIGN;
if (bitrate % alignment) {
bitrate += alignment - (bitrate % alignment);
}
dev_info(dphy->dev, "want dphy hs_rate %dMbps\n", bitrate/1000000);
p = m31_dphy_configs;
for (i = 0; i < ARRAY_SIZE(m31_dphy_configs); i++, p++) {
if (p->bitrate == bitrate) {
dev_info(dphy->dev, "config dphy hs_rate %dMbps\n", bitrate/1000000);
sf_dphy_set_reg(dphy->topsys + 0x64, M31_DPHY_REFCLK, REFCLK_IN_SEL_SHIFT, REFCLK_IN_SEL_MASK);
dev_info(dphy->dev, "MIPI dphy-tx # AON_POWER_READY_N active(%d)\n", AON_POWER_READY_N_active);
sf_dphy_set_reg(dphy->topsys, AON_POWER_READY_N_active,
AON_POWER_READY_N_SHIFT, AON_POWER_READY_N_MASK);
sf_dphy_set_reg(dphy->topsys, 0x0,
CFG_L0_SWAP_SEL_SHIFT, CFG_L0_SWAP_SEL_MASK);//Lane setting
sf_dphy_set_reg(dphy->topsys, 0x1,
CFG_L1_SWAP_SEL_SHIFT, CFG_L1_SWAP_SEL_MASK);
sf_dphy_set_reg(dphy->topsys, 0x4,
CFG_L2_SWAP_SEL_SHIFT, CFG_L2_SWAP_SEL_MASK);
sf_dphy_set_reg(dphy->topsys, 0x2,
CFG_L3_SWAP_SEL_SHIFT, CFG_L3_SWAP_SEL_MASK);
sf_dphy_set_reg(dphy->topsys, 0x3,
CFG_L4_SWAP_SEL_SHIFT, CFG_L4_SWAP_SEL_MASK);
//PLL setting
sf_dphy_set_reg(dphy->topsys + 0x1c, 0x0,
RG_CDTX_PLL_SSC_EN_SHIFT, RG_CDTX_PLL_SSC_EN_MASK);
sf_dphy_set_reg(dphy->topsys + 0x18, 0x1,
RG_CDTX_PLL_LDO_STB_X2_EN_SHIFT, RG_CDTX_PLL_LDO_STB_X2_EN_MASK);
sf_dphy_set_reg(dphy->topsys + 0x18, 0x1,
RG_CDTX_PLL_FM_EN_SHIFT, RG_CDTX_PLL_FM_EN_MASK);
sf_dphy_set_reg(dphy->topsys + 0x18, p->pll_prev_div,
RG_CDTX_PLL_PRE_DIV_SHIFT, RG_CDTX_PLL_PRE_DIV_MASK);
sf_dphy_set_reg(dphy->topsys + 0x18, p->pll_fbk_int,
RG_CDTX_PLL_FBK_INT_SHIFT, RG_CDTX_PLL_FBK_INT_MASK);
sf_dphy_set_reg(dphy->topsys + 0x14, p->pll_fbk_fra,
RG_CDTX_PLL_FBK_FRA_SHIFT, RG_CDTX_PLL_FBK_FRA_MASK);
sf_dphy_set_reg(dphy->topsys + 0x28, p->extd_cycle_sel,
RG_EXTD_CYCLE_SEL_SHIFT, RG_EXTD_CYCLE_SEL_MASK);
sf_dphy_set_reg(dphy->topsys + 0x24, p->dlane_hs_pre_time,
RG_DLANE_HS_PRE_TIME_SHIFT, RG_DLANE_HS_PRE_TIME_MASK);
sf_dphy_set_reg(dphy->topsys + 0x24, p->dlane_hs_pre_time,
RG_DLANE_HS_PRE_TIME_SHIFT, RG_DLANE_HS_PRE_TIME_MASK);
sf_dphy_set_reg(dphy->topsys + 0x24, p->dlane_hs_zero_time,
RG_DLANE_HS_ZERO_TIME_SHIFT, RG_DLANE_HS_ZERO_TIME_MASK);
sf_dphy_set_reg(dphy->topsys + 0x24, p->dlane_hs_trail_time,
RG_DLANE_HS_TRAIL_TIME_SHIFT, RG_DLANE_HS_TRAIL_TIME_MASK);
sf_dphy_set_reg(dphy->topsys + 0x20, p->clane_hs_pre_time,
RG_CLANE_HS_PRE_TIME_SHIFT, RG_CLANE_HS_PRE_TIME_MASK);
sf_dphy_set_reg(dphy->topsys + 0x24, p->clane_hs_zero_time,
RG_CLANE_HS_ZERO_TIME_SHIFT, RG_CLANE_HS_ZERO_TIME_MASK);
sf_dphy_set_reg(dphy->topsys + 0x20, p->clane_hs_trail_time,
RG_CLANE_HS_TRAIL_TIME_SHIFT, RG_CLANE_HS_TRAIL_TIME_MASK);
sf_dphy_set_reg(dphy->topsys + 0x20, p->clane_hs_clk_pre_time,
RG_CLANE_HS_CLK_PRE_TIME_SHIFT, RG_CLANE_HS_CLK_PRE_TIME_MASK);
sf_dphy_set_reg(dphy->topsys + 0x20, p->clane_hs_clk_post_time,
RG_CLANE_HS_CLK_POST_TIME_SHIFT, RG_CLANE_HS_CLK_POST_TIME_MASK);
break;
}
}
return -ENOTSUPP;
}
static int sf_dphy_power_on(struct phy *phy)
{
struct sf_dphy *dphy = phy_get_drvdata(phy);
int ret;
reset(0, dphy);
sf_dphy_set_reg(dphy->topsys + 0x30, 0,
SCFG_PPI_C_READY_SEL_SHIFT, SCFG_PPI_C_READY_SEL_MASK);
sf_dphy_set_reg(dphy->topsys + 0x30, 0,
SCFG_DSI_TXREADY_ESC_SEL_SHIFT, SCFG_DSI_TXREADY_ESC_SEL_MASK);
sf_dphy_set_reg(dphy->topsys + 0x2c, 0x30,
SCFG_C_HS_PRE_ZERO_TIME_SHIFT, SCFG_C_HS_PRE_ZERO_TIME_MASK);
ret = sf_dphy_clkrst_ena_deas(dphy->dev, dphy);//clk rst interface enable and deassert
return 0;
}
static int sf_dphy_power_off(struct phy *phy)
{
struct sf_dphy *dphy = phy_get_drvdata(phy);
sf_dphy_clkrst_disa_assert(dphy->dev, dphy);
reset(1, dphy);
return 0;
}
static int sf_dphy_init(struct phy *phy)
{
return 0;
}
static int sf_dphy_validate(struct phy *phy, enum phy_mode mode, int submode,
union phy_configure_opts *opts)
{
return 0;
}
static int sf_dphy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
{
return 0;
}
static int sf_dphy_exit(struct phy *phy)
{
return 0;
}
static const struct phy_ops sf_dphy_ops = {
.power_on = sf_dphy_power_on,
.power_off = sf_dphy_power_off,
.init = sf_dphy_init,
.exit = sf_dphy_exit,
//.configure = sf_dphy_configure,
.configure = sys_m31_dphy_tx_configure,
.validate = sf_dphy_validate,
.set_mode = sf_dphy_set_mode,
.owner = THIS_MODULE,
};
static const struct of_device_id sf_dphy_dt_ids[] = {
{
.compatible = "m31,mipi-dphy-tx",
},
{}
};
MODULE_DEVICE_TABLE(of, sf_dphy_dt_ids);
static int sf_dphy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct sf_dphy *dphy;
struct resource *res;
int ret;
uint32_t temp;
dev_info(&pdev->dev, "sf_dphy_probe begin\n");
dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
if (!dphy)
return -ENOMEM;
dev_set_drvdata(&pdev->dev, dphy);
dev_info(&pdev->dev, "===> %s enter, %d \n", __func__, __LINE__);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dphy->topsys = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dphy->topsys))
return PTR_ERR(dphy->topsys);
dphy->phy = devm_phy_create(&pdev->dev, NULL, &sf_dphy_ops);
if (IS_ERR(dphy->phy)) {
dev_err(&pdev->dev, "failed to create phy\n");
return PTR_ERR(dphy->phy);
}
phy_set_drvdata(dphy->phy, dphy);
dphy->dev = &pdev->dev;
// this power switch control bit was added in ECO, check ECO item "aon psw_en" for detail
dev_info(dphy->dev, "control ECO\n");
dphy->aonsys = ioremap(0x17010000, 0x10000);
temp = 0;
temp = sf_dphy_get_reg(dphy->aonsys, AON_GP_REG_SHIFT,AON_GP_REG_MASK);
dev_info(dphy->dev, "GET_AON_GP_REG\n");
if (!(temp & DPHY_TX_PSW_EN_MASK)) {
temp |= DPHY_TX_PSW_EN_MASK;
sf_dphy_set_reg(dphy->aonsys, temp,AON_GP_REG_SHIFT,AON_GP_REG_MASK);
}
dev_info(dphy->dev, "control ECO\n");
//mipi_pmic setting
dphy->mipitx_1p8 = devm_regulator_get(&pdev->dev, "mipitx_1p8");
if (IS_ERR(dphy->mipitx_1p8))
return PTR_ERR(dphy->mipitx_1p8);
dphy->mipitx_0p9 = devm_regulator_get(&pdev->dev, "mipitx_0p9");
if (IS_ERR(dphy->mipitx_0p9))
return PTR_ERR(dphy->mipitx_0p9);
//pmic turn on
ret = regulator_enable(dphy->mipitx_0p9);
if (ret) {
dev_err(&pdev->dev, "Cannot enable mipitx_0p9 regulator\n");
//goto err_reg_0p9;
}
udelay(100);
ret = regulator_enable(dphy->mipitx_1p8);
if (ret) {
dev_err(&pdev->dev, "Cannot enable mipitx_1p8 regulator\n");
//goto err_reg_1p8;
}
udelay(100);
//mipi_pmic setting
ret = sf_dphy_clkrst_get(&pdev->dev, dphy);
phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
dev_info(&pdev->dev, "sf_dphy_probe end\n");
return PTR_ERR_OR_ZERO(phy_provider);
}
static struct platform_driver sf_dphy_driver = {
.probe = sf_dphy_probe,
.driver = {
.name = "sf-mipi-dphy-tx",
.of_match_table = sf_dphy_dt_ids,
},
};
module_platform_driver(sf_dphy_driver);
MODULE_AUTHOR("Ezequiel Garcia <ezequiel@collabora.com>");
MODULE_DESCRIPTION("sf MIPI DPHY TX0 driver");
MODULE_LICENSE("Dual MIT/GPL");

View file

@ -419,6 +419,7 @@ extern "C" {
#define DRM_FORMAT_MOD_VENDOR_ARM 0x08
#define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09
#define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a
#define DRM_FORMAT_MOD_VENDOR_VS 0x0b
/* add more to the end as needed */
@ -1562,6 +1563,88 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier)
#define AMD_FMT_MOD_CLEAR(field) \
(~((__u64)AMD_FMT_MOD_##field##_MASK << AMD_FMT_MOD_##field##_SHIFT))
#define DRM_FORMAT_MOD_VS_TYPE_NORMAL 0x00
#define DRM_FORMAT_MOD_VS_TYPE_COMPRESSED 0x01
#define DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT 0x02
#define DRM_FORMAT_MOD_VS_TYPE_MASK ((__u64)0x3 << 54)
#define fourcc_mod_vs_code(type, val) \
fourcc_mod_code(VS, ((((__u64)type) << 54) | (val)))
#define DRM_FORMAT_MOD_VS_DEC_TILE_MODE_MASK 0x3F
#define DRM_FORMAT_MOD_VS_DEC_TILE_8X8_XMAJOR 0x00
#define DRM_FORMAT_MOD_VS_DEC_TILE_8X8_YMAJOR 0x01
#define DRM_FORMAT_MOD_VS_DEC_TILE_16X4 0x02
#define DRM_FORMAT_MOD_VS_DEC_TILE_8X4 0x03
#define DRM_FORMAT_MOD_VS_DEC_TILE_4X8 0x04
#define DRM_FORMAT_MOD_VS_DEC_RASTER_16X4 0x06
#define DRM_FORMAT_MOD_VS_DEC_TILE_64X4 0x07
#define DRM_FORMAT_MOD_VS_DEC_TILE_32X4 0x08
#define DRM_FORMAT_MOD_VS_DEC_RASTER_256X1 0x09
#define DRM_FORMAT_MOD_VS_DEC_RASTER_128X1 0x0A
#define DRM_FORMAT_MOD_VS_DEC_RASTER_64X4 0x0B
#define DRM_FORMAT_MOD_VS_DEC_RASTER_256X2 0x0C
#define DRM_FORMAT_MOD_VS_DEC_RASTER_128X2 0x0D
#define DRM_FORMAT_MOD_VS_DEC_RASTER_128X4 0x0E
#define DRM_FORMAT_MOD_VS_DEC_RASTER_64X1 0x0F
#define DRM_FORMAT_MOD_VS_DEC_TILE_16X8 0x10
#define DRM_FORMAT_MOD_VS_DEC_TILE_8X16 0x11
#define DRM_FORMAT_MOD_VS_DEC_RASTER_512X1 0x12
#define DRM_FORMAT_MOD_VS_DEC_RASTER_32X4 0x13
#define DRM_FORMAT_MOD_VS_DEC_RASTER_64X2 0x14
#define DRM_FORMAT_MOD_VS_DEC_RASTER_32X2 0x15
#define DRM_FORMAT_MOD_VS_DEC_RASTER_32X1 0x16
#define DRM_FORMAT_MOD_VS_DEC_RASTER_16X1 0x17
#define DRM_FORMAT_MOD_VS_DEC_TILE_128X4 0x18
#define DRM_FORMAT_MOD_VS_DEC_TILE_256X4 0x19
#define DRM_FORMAT_MOD_VS_DEC_TILE_512X4 0x1A
#define DRM_FORMAT_MOD_VS_DEC_TILE_16X16 0x1B
#define DRM_FORMAT_MOD_VS_DEC_TILE_32X16 0x1C
#define DRM_FORMAT_MOD_VS_DEC_TILE_64X16 0x1D
#define DRM_FORMAT_MOD_VS_DEC_TILE_128X8 0x1E
#define DRM_FORMAT_MOD_VS_DEC_TILE_8X4_S 0x1F
#define DRM_FORMAT_MOD_VS_DEC_TILE_16X4_S 0x20
#define DRM_FORMAT_MOD_VS_DEC_TILE_32X4_S 0x21
#define DRM_FORMAT_MOD_VS_DEC_TILE_16X4_LSB 0x22
#define DRM_FORMAT_MOD_VS_DEC_TILE_32X4_LSB 0x23
#define DRM_FORMAT_MOD_VS_DEC_TILE_32X8 0x24
#define DRM_FORMAT_MOD_VS_DEC_ALIGN_32 (0x01 << 6)
#define DRM_FORMAT_MOD_VS_DEC_ALIGN_64 (0x01 << 7)
#define fourcc_mod_vs_dec_code(tile, align) \
fourcc_mod_vs_code(DRM_FORMAT_MOD_VS_TYPE_COMPRESSED, \
((tile) | (align)))
#define DRM_FORMAT_MOD_VS_NORM_MODE_MASK 0x1F
#define DRM_FORMAT_MOD_VS_LINEAR 0x00
#define DRM_FORMAT_MOD_VS_TILED4x4 0x01
#define DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR 0x02
#define DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR 0x03
#define DRM_FORMAT_MOD_VS_TILE_8X8 0x04
#define DRM_FORMAT_MOD_VS_TILE_MODE1 0x05
#define DRM_FORMAT_MOD_VS_TILE_MODE2 0x06
#define DRM_FORMAT_MOD_VS_TILE_8X4 0x07
#define DRM_FORMAT_MOD_VS_TILE_MODE4 0x08
#define DRM_FORMAT_MOD_VS_TILE_MODE5 0x09
#define DRM_FORMAT_MOD_VS_TILE_MODE6 0x0A
#define DRM_FORMAT_MOD_VS_SUPER_TILED_XMAJOR_8X4 0x0B
#define DRM_FORMAT_MOD_VS_SUPER_TILED_YMAJOR_4X8 0x0C
#define DRM_FORMAT_MOD_VS_TILE_Y 0x0D
#define DRM_FORMAT_MOD_VS_TILE_128X1 0x0F
#define DRM_FORMAT_MOD_VS_TILE_256X1 0x10
#define DRM_FORMAT_MOD_VS_TILE_32X1 0x11
#define DRM_FORMAT_MOD_VS_TILE_64X1 0x12
#define DRM_FORMAT_MOD_VS_TILE_MODE4X4 0x15
#define fourcc_mod_vs_norm_code(tile) \
fourcc_mod_vs_code(DRM_FORMAT_MOD_VS_TYPE_NORMAL, \
(tile))
#define fourcc_mod_vs_custom_code(tile) \
fourcc_mod_vs_code(DRM_FORMAT_MOD_VS_TYPE_CUSTOM_10BIT, \
(tile))
#if defined(__cplusplus)
}
#endif

50
include/uapi/drm/vs_drm.h Normal file
View file

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
/*
* Copyright (C) 2020 VeriSilicon Holdings Co., Ltd.
*/
#ifndef __VS_DRM_H__
#define __VS_DRM_H__
#include "drm.h"
enum drm_vs_degamma_mode {
VS_DEGAMMA_DISABLE = 0,
VS_DEGAMMA_BT709 = 1,
VS_DEGAMMA_BT2020 = 2,
};
enum drm_vs_sync_dc_mode {
VS_SINGLE_DC = 0,
VS_MULTI_DC_PRIMARY = 1,
VS_MULTI_DC_SECONDARY = 2,
};
enum drm_vs_mmu_prefetch_mode {
VS_MMU_PREFETCH_DISABLE = 0,
VS_MMU_PREFETCH_ENABLE = 1,
};
struct drm_vs_watermark {
__u32 watermark;
__u8 qos_low;
__u8 qos_high;
};
struct drm_vs_color_mgmt {
__u32 colorkey;
__u32 colorkey_high;
__u32 clear_value;
bool clear_enable;
bool transparency;
};
struct drm_vs_roi {
bool enable;
__u16 roi_x;
__u16 roi_y;
__u16 roi_w;
__u16 roi_h;
};
#endif /* __VS_DRM_H__ */