mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-03-16 04:04:06 +00:00
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:
parent
dce2b71254
commit
c8bc7c051d
52 changed files with 17580 additions and 68 deletions
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
40
drivers/gpu/drm/i2c/tda998x_pin.c
Normal file
40
drivers/gpu/drm/i2c/tda998x_pin.c
Normal 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");
|
57
drivers/gpu/drm/verisilicon/Kconfig
Normal file
57
drivers/gpu/drm/verisilicon/Kconfig
Normal 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"
|
19
drivers/gpu/drm/verisilicon/Makefile
Normal file
19
drivers/gpu/drm/verisilicon/Makefile
Normal 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
|
||||
|
118
drivers/gpu/drm/verisilicon/README
Normal file
118
drivers/gpu/drm/verisilicon/README
Normal 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
|
25
drivers/gpu/drm/verisilicon/adv7511/Kconfig
Normal file
25
drivers/gpu/drm/verisilicon/adv7511/Kconfig
Normal 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.
|
5
drivers/gpu/drm/verisilicon/adv7511/Makefile
Normal file
5
drivers/gpu/drm/verisilicon/adv7511/Makefile
Normal 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
|
420
drivers/gpu/drm/verisilicon/adv7511/adv7511.h
Normal file
420
drivers/gpu/drm/verisilicon/adv7511/adv7511.h
Normal 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__ */
|
250
drivers/gpu/drm/verisilicon/adv7511/adv7511_audio.c
Normal file
250
drivers/gpu/drm/verisilicon/adv7511/adv7511_audio.c
Normal 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;
|
||||
}
|
||||
}
|
349
drivers/gpu/drm/verisilicon/adv7511/adv7511_cec.c
Normal file
349
drivers/gpu/drm/verisilicon/adv7511/adv7511_cec.c
Normal 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;
|
||||
}
|
1384
drivers/gpu/drm/verisilicon/adv7511/adv7511_drv.c
Normal file
1384
drivers/gpu/drm/verisilicon/adv7511/adv7511_drv.c
Normal file
File diff suppressed because it is too large
Load diff
215
drivers/gpu/drm/verisilicon/adv7511/adv7533.c
Normal file
215
drivers/gpu/drm/verisilicon/adv7511/adv7533.c
Normal 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;
|
||||
}
|
1086
drivers/gpu/drm/verisilicon/inno_hdmi.c
Normal file
1086
drivers/gpu/drm/verisilicon/inno_hdmi.c
Normal file
File diff suppressed because it is too large
Load diff
599
drivers/gpu/drm/verisilicon/inno_hdmi.h
Normal file
599
drivers/gpu/drm/verisilicon/inno_hdmi.h
Normal 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__ */
|
1631
drivers/gpu/drm/verisilicon/starfive_drm_dsi.c
Normal file
1631
drivers/gpu/drm/verisilicon/starfive_drm_dsi.c
Normal file
File diff suppressed because it is too large
Load diff
528
drivers/gpu/drm/verisilicon/starfive_drm_seeedpanel.c
Normal file
528
drivers/gpu/drm/verisilicon/starfive_drm_seeedpanel.c
Normal 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, ®_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, ®_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");
|
||||
|
293
drivers/gpu/drm/verisilicon/starfive_hdmi_audio.c
Normal file
293
drivers/gpu/drm/verisilicon/starfive_hdmi_audio.c
Normal 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;
|
||||
}
|
470
drivers/gpu/drm/verisilicon/vs_crtc.c
Normal file
470
drivers/gpu/drm/verisilicon/vs_crtc.c
Normal 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;
|
||||
}
|
74
drivers/gpu/drm/verisilicon/vs_crtc.h
Normal file
74
drivers/gpu/drm/verisilicon/vs_crtc.h
Normal 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__ */
|
1608
drivers/gpu/drm/verisilicon/vs_dc.c
Normal file
1608
drivers/gpu/drm/verisilicon/vs_dc.c
Normal file
File diff suppressed because it is too large
Load diff
91
drivers/gpu/drm/verisilicon/vs_dc.h
Normal file
91
drivers/gpu/drm/verisilicon/vs_dc.h
Normal 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__ */
|
386
drivers/gpu/drm/verisilicon/vs_dc_dec.c
Normal file
386
drivers/gpu/drm/verisilicon/vs_dc_dec.c
Normal 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;
|
||||
}
|
106
drivers/gpu/drm/verisilicon/vs_dc_dec.h
Normal file
106
drivers/gpu/drm/verisilicon/vs_dc_dec.h
Normal 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_ */
|
2223
drivers/gpu/drm/verisilicon/vs_dc_hw.c
Normal file
2223
drivers/gpu/drm/verisilicon/vs_dc_hw.c
Normal file
File diff suppressed because it is too large
Load diff
580
drivers/gpu/drm/verisilicon/vs_dc_hw.h
Normal file
580
drivers/gpu/drm/verisilicon/vs_dc_hw.h
Normal 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__ */
|
710
drivers/gpu/drm/verisilicon/vs_dc_mmu.c
Normal file
710
drivers/gpu/drm/verisilicon/vs_dc_mmu.c
Normal 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);
|
||||
}
|
98
drivers/gpu/drm/verisilicon/vs_dc_mmu.h
Normal file
98
drivers/gpu/drm/verisilicon/vs_dc_mmu.h
Normal 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_ */
|
483
drivers/gpu/drm/verisilicon/vs_drv.c
Normal file
483
drivers/gpu/drm/verisilicon/vs_drv.c
Normal 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");
|
72
drivers/gpu/drm/verisilicon/vs_drv.h
Normal file
72
drivers/gpu/drm/verisilicon/vs_drv.h
Normal 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__ */
|
192
drivers/gpu/drm/verisilicon/vs_fb.c
Normal file
192
drivers/gpu/drm/verisilicon/vs_fb.c
Normal 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;
|
||||
}
|
13
drivers/gpu/drm/verisilicon/vs_fb.h
Normal file
13
drivers/gpu/drm/verisilicon/vs_fb.h
Normal 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__ */
|
552
drivers/gpu/drm/verisilicon/vs_gem.c
Normal file
552
drivers/gpu/drm/verisilicon/vs_gem.c
Normal 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);
|
||||
}
|
74
drivers/gpu/drm/verisilicon/vs_gem.h
Normal file
74
drivers/gpu/drm/verisilicon/vs_gem.h
Normal 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__ */
|
526
drivers/gpu/drm/verisilicon/vs_plane.c
Normal file
526
drivers/gpu/drm/verisilicon/vs_plane.c
Normal 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;
|
||||
}
|
81
drivers/gpu/drm/verisilicon/vs_plane.h
Normal file
81
drivers/gpu/drm/verisilicon/vs_plane.h
Normal 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__ */
|
312
drivers/gpu/drm/verisilicon/vs_simple_enc.c
Normal file
312
drivers/gpu/drm/verisilicon/vs_simple_enc.c
Normal 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");
|
27
drivers/gpu/drm/verisilicon/vs_simple_enc.h
Normal file
27
drivers/gpu/drm/verisilicon/vs_simple_enc.h
Normal 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_ */
|
75
drivers/gpu/drm/verisilicon/vs_type.h
Normal file
75
drivers/gpu/drm/verisilicon/vs_type.h
Normal 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__ */
|
363
drivers/gpu/drm/verisilicon/vs_virtual.c
Normal file
363
drivers/gpu/drm/verisilicon/vs_virtual.c
Normal 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");
|
37
drivers/gpu/drm/verisilicon/vs_virtual.h
Normal file
37
drivers/gpu/drm/verisilicon/vs_virtual.h
Normal 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_ */
|
|
@ -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
|
||||
|
|
|
@ -35,4 +35,5 @@ obj-y += allwinner/ \
|
|||
sunplus/ \
|
||||
tegra/ \
|
||||
ti/ \
|
||||
xilinx/
|
||||
xilinx/ \
|
||||
m31/
|
||||
|
|
396
drivers/phy/m31/7110-m31-dphy.h
Normal file
396
drivers/phy/m31/7110-m31-dphy.h
Normal 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
15
drivers/phy/m31/Kconfig
Normal 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
3
drivers/phy/m31/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_PHY_M31_DPHY_RX0) += phy-m31-dphy-tx0.o
|
||||
|
789
drivers/phy/m31/phy-m31-dphy-tx0.c
Normal file
789
drivers/phy/m31/phy-m31-dphy-tx0.c
Normal 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");
|
|
@ -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
50
include/uapi/drm/vs_drm.h
Normal 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__ */
|
Loading…
Add table
Reference in a new issue