提交 3a5e6bb9 编写于 作者: D Dave Airlie

Merge tag 'zxdrm-4.11' of...

Merge tag 'zxdrm-4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux into drm-next

ZTE DRM driver updates for 4.11:
 - Add missing selection of VIDEOMODE_HELPERS in Kconfig, since ZTE DRM
   driver uses drm_display_mode_to_videomode().
 - Enable HDMI audio support through SPDIF interface based on generic
   hdmi-audio-codec driver.
 - Enable VOU VL (Video Layer) to support overlay plane with scaling
   function.
 - Refine zx_vou driver a bit and then add TV Encoder output device
   support.

[airlied: fixup plane format change]

* tag 'zxdrm-4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux:
  drm: zte: add tvenc driver support
  dt: add bindings for ZTE tvenc device
  drm: zte: add function to configure vou_ctrl dividers
  drm: zte: move struct vou_inf into zx_vou driver
  drm: zte: add interlace mode support
  drm: zte: add overlay plane support
  drm: zte: add .atomic_disable hook to disable graphic layer
  drm: zte: make zx_plane accessible from zx_vou driver
  drm: zte: support hdmi audio through spdif
  drm: zte: select VIDEOMODE_HELPERS in Kconfig
......@@ -49,6 +49,15 @@ Required properties:
"osc_clk"
"xclk"
* TV Encoder output device
Required properties:
- compatible: should be "zte,zx296718-tvenc"
- reg: Physical base address and length of the TVENC device IO region
- zte,tvenc-power-control: the phandle to SYSCTRL block followed by two
integer cells. The first cell is the offset of SYSCTRL register used
to control TV Encoder DAC power, and the second cell is the bit mask.
Example:
vou: vou@1440000 {
......@@ -81,4 +90,10 @@ vou: vou@1440000 {
<&topcrm HDMI_XCLK>;
clock-names = "osc_cec", "osc_clk", "xclk";
};
tvenc: tvenc@2000 {
compatible = "zte,zx296718-tvenc";
reg = <0x2000 0x1000>;
zte,tvenc-power-control = <&sysctrl 0x170 0x10>;
};
};
......@@ -4,5 +4,7 @@ config DRM_ZTE
select DRM_KMS_CMA_HELPER
select DRM_KMS_FB_HELPER
select DRM_KMS_HELPER
select SND_SOC_HDMI_CODEC if SND_SOC
select VIDEOMODE_HELPERS
help
Choose this option to enable DRM on ZTE ZX SoCs.
......@@ -2,6 +2,7 @@ zxdrm-y := \
zx_drm_drv.o \
zx_hdmi.o \
zx_plane.o \
zx_tvenc.o \
zx_vou.o
obj-$(CONFIG_DRM_ZTE) += zxdrm.o
......@@ -247,6 +247,7 @@ static struct platform_driver zx_drm_platform_driver = {
static struct platform_driver *drivers[] = {
&zx_crtc_driver,
&zx_hdmi_driver,
&zx_tvenc_driver,
&zx_drm_platform_driver,
};
......
......@@ -13,6 +13,7 @@
extern struct platform_driver zx_crtc_driver;
extern struct platform_driver zx_hdmi_driver;
extern struct platform_driver zx_tvenc_driver;
static inline u32 zx_readl(void __iomem *reg)
{
......
......@@ -25,6 +25,8 @@
#include <drm/drm_of.h>
#include <drm/drmP.h>
#include <sound/hdmi-codec.h>
#include "zx_hdmi_regs.h"
#include "zx_vou.h"
......@@ -49,17 +51,11 @@ struct zx_hdmi {
bool sink_is_hdmi;
bool sink_has_audio;
const struct vou_inf *inf;
struct platform_device *audio_pdev;
};
#define to_zx_hdmi(x) container_of(x, struct zx_hdmi, x)
static const struct vou_inf vou_inf_hdmi = {
.id = VOU_HDMI,
.data_sel = VOU_YUV444,
.clocks_en_bits = BIT(24) | BIT(18) | BIT(6),
.clocks_sel_bits = BIT(13) | BIT(2),
};
static inline u8 hdmi_readb(struct zx_hdmi *hdmi, u16 offset)
{
return readl_relaxed(hdmi->mmio + offset * 4);
......@@ -238,14 +234,14 @@ static void zx_hdmi_encoder_enable(struct drm_encoder *encoder)
zx_hdmi_hw_enable(hdmi);
vou_inf_enable(hdmi->inf, encoder->crtc);
vou_inf_enable(VOU_HDMI, encoder->crtc);
}
static void zx_hdmi_encoder_disable(struct drm_encoder *encoder)
{
struct zx_hdmi *hdmi = to_zx_hdmi(encoder);
vou_inf_disable(hdmi->inf, encoder->crtc);
vou_inf_disable(VOU_HDMI, encoder->crtc);
zx_hdmi_hw_disable(hdmi);
......@@ -366,6 +362,142 @@ static irqreturn_t zx_hdmi_irq_handler(int irq, void *dev_id)
return IRQ_NONE;
}
static int zx_hdmi_audio_startup(struct device *dev, void *data)
{
struct zx_hdmi *hdmi = dev_get_drvdata(dev);
struct drm_encoder *encoder = &hdmi->encoder;
vou_inf_hdmi_audio_sel(encoder->crtc, VOU_HDMI_AUD_SPDIF);
return 0;
}
static void zx_hdmi_audio_shutdown(struct device *dev, void *data)
{
struct zx_hdmi *hdmi = dev_get_drvdata(dev);
/* Disable audio input */
hdmi_writeb_mask(hdmi, AUD_EN, AUD_IN_EN, 0);
}
static inline int zx_hdmi_audio_get_n(unsigned int fs)
{
unsigned int n;
if (fs && (fs % 44100) == 0)
n = 6272 * (fs / 44100);
else
n = fs * 128 / 1000;
return n;
}
static int zx_hdmi_audio_hw_params(struct device *dev,
void *data,
struct hdmi_codec_daifmt *daifmt,
struct hdmi_codec_params *params)
{
struct zx_hdmi *hdmi = dev_get_drvdata(dev);
struct hdmi_audio_infoframe *cea = &params->cea;
union hdmi_infoframe frame;
int n;
/* We only support spdif for now */
if (daifmt->fmt != HDMI_SPDIF) {
DRM_DEV_ERROR(hdmi->dev, "invalid daifmt %d\n", daifmt->fmt);
return -EINVAL;
}
switch (params->sample_width) {
case 16:
hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK,
SPDIF_SAMPLE_SIZE_16BIT);
break;
case 20:
hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK,
SPDIF_SAMPLE_SIZE_20BIT);
break;
case 24:
hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK,
SPDIF_SAMPLE_SIZE_24BIT);
break;
default:
DRM_DEV_ERROR(hdmi->dev, "invalid sample width %d\n",
params->sample_width);
return -EINVAL;
}
/* CTS is calculated by hardware, and we only need to take care of N */
n = zx_hdmi_audio_get_n(params->sample_rate);
hdmi_writeb(hdmi, N_SVAL1, n & 0xff);
hdmi_writeb(hdmi, N_SVAL2, (n >> 8) & 0xff);
hdmi_writeb(hdmi, N_SVAL3, (n >> 16) & 0xf);
/* Enable spdif mode */
hdmi_writeb_mask(hdmi, AUD_MODE, SPDIF_EN, SPDIF_EN);
/* Enable audio input */
hdmi_writeb_mask(hdmi, AUD_EN, AUD_IN_EN, AUD_IN_EN);
memcpy(&frame.audio, cea, sizeof(*cea));
return zx_hdmi_infoframe_trans(hdmi, &frame, FSEL_AUDIO);
}
static int zx_hdmi_audio_digital_mute(struct device *dev, void *data,
bool enable)
{
struct zx_hdmi *hdmi = dev_get_drvdata(dev);
if (enable)
hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, TPI_AUD_MUTE,
TPI_AUD_MUTE);
else
hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, TPI_AUD_MUTE, 0);
return 0;
}
static int zx_hdmi_audio_get_eld(struct device *dev, void *data,
uint8_t *buf, size_t len)
{
struct zx_hdmi *hdmi = dev_get_drvdata(dev);
struct drm_connector *connector = &hdmi->connector;
memcpy(buf, connector->eld, min(sizeof(connector->eld), len));
return 0;
}
static const struct hdmi_codec_ops zx_hdmi_codec_ops = {
.audio_startup = zx_hdmi_audio_startup,
.hw_params = zx_hdmi_audio_hw_params,
.audio_shutdown = zx_hdmi_audio_shutdown,
.digital_mute = zx_hdmi_audio_digital_mute,
.get_eld = zx_hdmi_audio_get_eld,
};
static struct hdmi_codec_pdata zx_hdmi_codec_pdata = {
.ops = &zx_hdmi_codec_ops,
.spdif = 1,
};
static int zx_hdmi_audio_register(struct zx_hdmi *hdmi)
{
struct platform_device *pdev;
pdev = platform_device_register_data(hdmi->dev, HDMI_CODEC_DRV_NAME,
PLATFORM_DEVID_AUTO,
&zx_hdmi_codec_pdata,
sizeof(zx_hdmi_codec_pdata));
if (IS_ERR(pdev))
return PTR_ERR(pdev);
hdmi->audio_pdev = pdev;
return 0;
}
static int zx_hdmi_i2c_read(struct zx_hdmi *hdmi, struct i2c_msg *msg)
{
int len = msg->len;
......@@ -523,7 +655,6 @@ static int zx_hdmi_bind(struct device *dev, struct device *master, void *data)
hdmi->dev = dev;
hdmi->drm = drm;
hdmi->inf = &vou_inf_hdmi;
dev_set_drvdata(dev, hdmi);
......@@ -566,6 +697,12 @@ static int zx_hdmi_bind(struct device *dev, struct device *master, void *data)
return ret;
}
ret = zx_hdmi_audio_register(hdmi);
if (ret) {
DRM_DEV_ERROR(dev, "failed to register audio: %d\n", ret);
return ret;
}
ret = zx_hdmi_register(drm, hdmi);
if (ret) {
DRM_DEV_ERROR(dev, "failed to register hdmi: %d\n", ret);
......@@ -590,6 +727,9 @@ static void zx_hdmi_unbind(struct device *dev, struct device *master,
hdmi->connector.funcs->destroy(&hdmi->connector);
hdmi->encoder.funcs->destroy(&hdmi->encoder);
if (hdmi->audio_pdev)
platform_device_unregister(hdmi->audio_pdev);
}
static const struct component_ops zx_hdmi_component_ops = {
......
......@@ -52,5 +52,19 @@
#define TPI_INFO_TRANS_RPT BIT(6)
#define TPI_DDC_MASTER_EN 0x06f8
#define HW_DDC_MASTER BIT(7)
#define N_SVAL1 0xa03
#define N_SVAL2 0xa04
#define N_SVAL3 0xa05
#define AUD_EN 0xa13
#define AUD_IN_EN BIT(0)
#define AUD_MODE 0xa14
#define SPDIF_EN BIT(1)
#define TPI_AUD_CONFIG 0xa62
#define SPDIF_SAMPLE_SIZE_SHIFT 6
#define SPDIF_SAMPLE_SIZE_MASK (0x3 << SPDIF_SAMPLE_SIZE_SHIFT)
#define SPDIF_SAMPLE_SIZE_16BIT (0x1 << SPDIF_SAMPLE_SIZE_SHIFT)
#define SPDIF_SAMPLE_SIZE_20BIT (0x2 << SPDIF_SAMPLE_SIZE_SHIFT)
#define SPDIF_SAMPLE_SIZE_24BIT (0x3 << SPDIF_SAMPLE_SIZE_SHIFT)
#define TPI_AUD_MUTE BIT(4)
#endif /* __ZX_HDMI_REGS_H__ */
......@@ -21,16 +21,6 @@
#include "zx_plane_regs.h"
#include "zx_vou.h"
struct zx_plane {
struct drm_plane plane;
void __iomem *layer;
void __iomem *csc;
void __iomem *hbsc;
void __iomem *rsz;
};
#define to_zx_plane(plane) container_of(plane, struct zx_plane, plane)
static const uint32_t gl_formats[] = {
DRM_FORMAT_ARGB8888,
DRM_FORMAT_XRGB8888,
......@@ -40,6 +30,261 @@ static const uint32_t gl_formats[] = {
DRM_FORMAT_ARGB4444,
};
static const uint32_t vl_formats[] = {
DRM_FORMAT_NV12, /* Semi-planar YUV420 */
DRM_FORMAT_YUV420, /* Planar YUV420 */
DRM_FORMAT_YUYV, /* Packed YUV422 */
DRM_FORMAT_YVYU,
DRM_FORMAT_UYVY,
DRM_FORMAT_VYUY,
DRM_FORMAT_YUV444, /* YUV444 8bit */
/*
* TODO: add formats below that HW supports:
* - YUV420 P010
* - YUV420 Hantro
* - YUV444 10bit
*/
};
#define FRAC_16_16(mult, div) (((mult) << 16) / (div))
static int zx_vl_plane_atomic_check(struct drm_plane *plane,
struct drm_plane_state *plane_state)
{
struct drm_framebuffer *fb = plane_state->fb;
struct drm_crtc *crtc = plane_state->crtc;
struct drm_crtc_state *crtc_state;
struct drm_rect clip;
int min_scale = FRAC_16_16(1, 8);
int max_scale = FRAC_16_16(8, 1);
if (!crtc || !fb)
return 0;
crtc_state = drm_atomic_get_existing_crtc_state(plane_state->state,
crtc);
if (WARN_ON(!crtc_state))
return -EINVAL;
/* nothing to check when disabling or disabled */
if (!crtc_state->enable)
return 0;
/* plane must be enabled */
if (!plane_state->crtc)
return -EINVAL;
clip.x1 = 0;
clip.y1 = 0;
clip.x2 = crtc_state->adjusted_mode.hdisplay;
clip.y2 = crtc_state->adjusted_mode.vdisplay;
return drm_plane_helper_check_state(plane_state, &clip,
min_scale, max_scale,
true, true);
}
static int zx_vl_get_fmt(uint32_t format)
{
switch (format) {
case DRM_FORMAT_NV12:
return VL_FMT_YUV420;
case DRM_FORMAT_YUV420:
return VL_YUV420_PLANAR | VL_FMT_YUV420;
case DRM_FORMAT_YUYV:
return VL_YUV422_YUYV | VL_FMT_YUV422;
case DRM_FORMAT_YVYU:
return VL_YUV422_YVYU | VL_FMT_YUV422;
case DRM_FORMAT_UYVY:
return VL_YUV422_UYVY | VL_FMT_YUV422;
case DRM_FORMAT_VYUY:
return VL_YUV422_VYUY | VL_FMT_YUV422;
case DRM_FORMAT_YUV444:
return VL_FMT_YUV444_8BIT;
default:
WARN_ONCE(1, "invalid pixel format %d\n", format);
return -EINVAL;
}
}
static inline void zx_vl_set_update(struct zx_plane *zplane)
{
void __iomem *layer = zplane->layer;
zx_writel_mask(layer + VL_CTRL0, VL_UPDATE, VL_UPDATE);
}
static inline void zx_vl_rsz_set_update(struct zx_plane *zplane)
{
zx_writel(zplane->rsz + RSZ_VL_ENABLE_CFG, 1);
}
static int zx_vl_rsz_get_fmt(uint32_t format)
{
switch (format) {
case DRM_FORMAT_NV12:
case DRM_FORMAT_YUV420:
return RSZ_VL_FMT_YCBCR420;
case DRM_FORMAT_YUYV:
case DRM_FORMAT_YVYU:
case DRM_FORMAT_UYVY:
case DRM_FORMAT_VYUY:
return RSZ_VL_FMT_YCBCR422;
case DRM_FORMAT_YUV444:
return RSZ_VL_FMT_YCBCR444;
default:
WARN_ONCE(1, "invalid pixel format %d\n", format);
return -EINVAL;
}
}
static inline u32 rsz_step_value(u32 src, u32 dst)
{
u32 val = 0;
if (src == dst)
val = 0;
else if (src < dst)
val = RSZ_PARA_STEP((src << 16) / dst);
else if (src > dst)
val = RSZ_DATA_STEP(src / dst) |
RSZ_PARA_STEP(((src << 16) / dst) & 0xffff);
return val;
}
static void zx_vl_rsz_setup(struct zx_plane *zplane, uint32_t format,
u32 src_w, u32 src_h, u32 dst_w, u32 dst_h)
{
void __iomem *rsz = zplane->rsz;
u32 src_chroma_w = src_w;
u32 src_chroma_h = src_h;
u32 fmt;
/* Set up source and destination resolution */
zx_writel(rsz + RSZ_SRC_CFG, RSZ_VER(src_h - 1) | RSZ_HOR(src_w - 1));
zx_writel(rsz + RSZ_DEST_CFG, RSZ_VER(dst_h - 1) | RSZ_HOR(dst_w - 1));
/* Configure data format for VL RSZ */
fmt = zx_vl_rsz_get_fmt(format);
if (fmt >= 0)
zx_writel_mask(rsz + RSZ_VL_CTRL_CFG, RSZ_VL_FMT_MASK, fmt);
/* Calculate Chroma height and width */
if (fmt == RSZ_VL_FMT_YCBCR420) {
src_chroma_w = src_w >> 1;
src_chroma_h = src_h >> 1;
} else if (fmt == RSZ_VL_FMT_YCBCR422) {
src_chroma_w = src_w >> 1;
}
/* Set up Luma and Chroma step registers */
zx_writel(rsz + RSZ_VL_LUMA_HOR, rsz_step_value(src_w, dst_w));
zx_writel(rsz + RSZ_VL_LUMA_VER, rsz_step_value(src_h, dst_h));
zx_writel(rsz + RSZ_VL_CHROMA_HOR, rsz_step_value(src_chroma_w, dst_w));
zx_writel(rsz + RSZ_VL_CHROMA_VER, rsz_step_value(src_chroma_h, dst_h));
zx_vl_rsz_set_update(zplane);
}
static void zx_vl_plane_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
struct zx_plane *zplane = to_zx_plane(plane);
struct drm_plane_state *state = plane->state;
struct drm_framebuffer *fb = state->fb;
struct drm_rect *src = &state->src;
struct drm_rect *dst = &state->dst;
struct drm_gem_cma_object *cma_obj;
void __iomem *layer = zplane->layer;
void __iomem *hbsc = zplane->hbsc;
void __iomem *paddr_reg;
dma_addr_t paddr;
u32 src_x, src_y, src_w, src_h;
u32 dst_x, dst_y, dst_w, dst_h;
uint32_t format;
u32 fmt;
int num_planes;
int i;
if (!fb)
return;
format = fb->format->format;
src_x = src->x1 >> 16;
src_y = src->y1 >> 16;
src_w = drm_rect_width(src) >> 16;
src_h = drm_rect_height(src) >> 16;
dst_x = dst->x1;
dst_y = dst->y1;
dst_w = drm_rect_width(dst);
dst_h = drm_rect_height(dst);
/* Set up data address registers for Y, Cb and Cr planes */
num_planes = drm_format_num_planes(format);
paddr_reg = layer + VL_Y;
for (i = 0; i < num_planes; i++) {
cma_obj = drm_fb_cma_get_gem_obj(fb, i);
paddr = cma_obj->paddr + fb->offsets[i];
paddr += src_y * fb->pitches[i];
paddr += src_x * drm_format_plane_cpp(format, i);
zx_writel(paddr_reg, paddr);
paddr_reg += 4;
}
/* Set up source height/width register */
zx_writel(layer + VL_SRC_SIZE, GL_SRC_W(src_w) | GL_SRC_H(src_h));
/* Set up start position register */
zx_writel(layer + VL_POS_START, GL_POS_X(dst_x) | GL_POS_Y(dst_y));
/* Set up end position register */
zx_writel(layer + VL_POS_END,
GL_POS_X(dst_x + dst_w) | GL_POS_Y(dst_y + dst_h));
/* Strides of Cb and Cr planes should be identical */
zx_writel(layer + VL_STRIDE, LUMA_STRIDE(fb->pitches[0]) |
CHROMA_STRIDE(fb->pitches[1]));
/* Set up video layer data format */
fmt = zx_vl_get_fmt(format);
if (fmt >= 0)
zx_writel(layer + VL_CTRL1, fmt);
/* Always use scaler since it exists (set for not bypass) */
zx_writel_mask(layer + VL_CTRL2, VL_SCALER_BYPASS_MODE,
VL_SCALER_BYPASS_MODE);
zx_vl_rsz_setup(zplane, format, src_w, src_h, dst_w, dst_h);
/* Enable HBSC block */
zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, HBSC_CTRL_EN);
zx_vou_layer_enable(plane);
zx_vl_set_update(zplane);
}
static void zx_plane_atomic_disable(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
struct zx_plane *zplane = to_zx_plane(plane);
void __iomem *hbsc = zplane->hbsc;
zx_vou_layer_disable(plane);
/* Disable HBSC block */
zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, 0);
}
static const struct drm_plane_helper_funcs zx_vl_plane_helper_funcs = {
.atomic_check = zx_vl_plane_atomic_check,
.atomic_update = zx_vl_plane_atomic_update,
.atomic_disable = zx_plane_atomic_disable,
};
static int zx_gl_plane_atomic_check(struct drm_plane *plane,
struct drm_plane_state *plane_state)
{
......@@ -107,14 +352,6 @@ static inline void zx_gl_rsz_set_update(struct zx_plane *zplane)
zx_writel(zplane->rsz + RSZ_ENABLE_CFG, 1);
}
void zx_plane_set_update(struct drm_plane *plane)
{
struct zx_plane *zplane = to_zx_plane(plane);
zx_gl_rsz_set_update(zplane);
zx_gl_set_update(zplane);
}
static void zx_gl_rsz_setup(struct zx_plane *zplane, u32 src_w, u32 src_h,
u32 dst_w, u32 dst_h)
{
......@@ -207,12 +444,15 @@ static void zx_gl_plane_atomic_update(struct drm_plane *plane,
/* Enable HBSC block */
zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, HBSC_CTRL_EN);
zx_vou_layer_enable(plane);
zx_gl_set_update(zplane);
}
static const struct drm_plane_helper_funcs zx_gl_plane_helper_funcs = {
.atomic_check = zx_gl_plane_atomic_check,
.atomic_update = zx_gl_plane_atomic_update,
.atomic_disable = zx_plane_atomic_disable,
};
static void zx_plane_destroy(struct drm_plane *plane)
......@@ -230,6 +470,28 @@ static const struct drm_plane_funcs zx_plane_funcs = {
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
void zx_plane_set_update(struct drm_plane *plane)
{
struct zx_plane *zplane = to_zx_plane(plane);
/* Do nothing if the plane is not enabled */
if (!plane->state->crtc)
return;
switch (plane->type) {
case DRM_PLANE_TYPE_PRIMARY:
zx_gl_rsz_set_update(zplane);
zx_gl_set_update(zplane);
break;
case DRM_PLANE_TYPE_OVERLAY:
zx_vl_rsz_set_update(zplane);
zx_vl_set_update(zplane);
break;
default:
WARN_ONCE(1, "unsupported plane type %d\n", plane->type);
}
}
static void zx_plane_hbsc_init(struct zx_plane *zplane)
{
void __iomem *hbsc = zplane->hbsc;
......@@ -248,28 +510,16 @@ static void zx_plane_hbsc_init(struct zx_plane *zplane)
zx_writel(hbsc + HBSC_THRESHOLD_COL3, (0x3c0 << 16) | 0x40);
}
struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev,
struct zx_layer_data *data,
enum drm_plane_type type)
int zx_plane_init(struct drm_device *drm, struct zx_plane *zplane,
enum drm_plane_type type)
{
const struct drm_plane_helper_funcs *helper;
struct zx_plane *zplane;
struct drm_plane *plane;
struct drm_plane *plane = &zplane->plane;
struct device *dev = zplane->dev;
const uint32_t *formats;
unsigned int format_count;
int ret;
zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL);
if (!zplane)
return ERR_PTR(-ENOMEM);
plane = &zplane->plane;
zplane->layer = data->layer;
zplane->hbsc = data->hbsc;
zplane->csc = data->csc;
zplane->rsz = data->rsz;
zx_plane_hbsc_init(zplane);
switch (type) {
......@@ -279,10 +529,12 @@ struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev,
format_count = ARRAY_SIZE(gl_formats);
break;
case DRM_PLANE_TYPE_OVERLAY:
/* TODO: add video layer (vl) support */
helper = &zx_vl_plane_helper_funcs;
formats = vl_formats;
format_count = ARRAY_SIZE(vl_formats);
break;
default:
return ERR_PTR(-ENODEV);
return -ENODEV;
}
ret = drm_universal_plane_init(drm, plane, VOU_CRTC_MASK,
......@@ -290,10 +542,10 @@ struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev,
type, NULL);
if (ret) {
DRM_DEV_ERROR(dev, "failed to init universal plane: %d\n", ret);
return ERR_PTR(ret);
return ret;
}
drm_plane_helper_add(plane, helper);
return plane;
return 0;
}
......@@ -11,16 +11,20 @@
#ifndef __ZX_PLANE_H__
#define __ZX_PLANE_H__
struct zx_layer_data {
struct zx_plane {
struct drm_plane plane;
struct device *dev;
void __iomem *layer;
void __iomem *csc;
void __iomem *hbsc;
void __iomem *rsz;
const struct vou_layer_bits *bits;
};
struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev,
struct zx_layer_data *data,
enum drm_plane_type type);
#define to_zx_plane(plane) container_of(plane, struct zx_plane, plane)
int zx_plane_init(struct drm_device *drm, struct zx_plane *zplane,
enum drm_plane_type type);
void zx_plane_set_update(struct drm_plane *plane);
#endif /* __ZX_PLANE_H__ */
......@@ -46,6 +46,37 @@
#define GL_POS_X(x) (((x) << GL_POS_X_SHIFT) & GL_POS_X_MASK)
#define GL_POS_Y(x) (((x) << GL_POS_Y_SHIFT) & GL_POS_Y_MASK)
/* VL registers */
#define VL_CTRL0 0x00
#define VL_UPDATE BIT(3)
#define VL_CTRL1 0x04
#define VL_YUV420_PLANAR BIT(5)
#define VL_YUV422_SHIFT 3
#define VL_YUV422_YUYV (0 << VL_YUV422_SHIFT)
#define VL_YUV422_YVYU (1 << VL_YUV422_SHIFT)
#define VL_YUV422_UYVY (2 << VL_YUV422_SHIFT)
#define VL_YUV422_VYUY (3 << VL_YUV422_SHIFT)
#define VL_FMT_YUV420 0
#define VL_FMT_YUV422 1
#define VL_FMT_YUV420_P010 2
#define VL_FMT_YUV420_HANTRO 3
#define VL_FMT_YUV444_8BIT 4
#define VL_FMT_YUV444_10BIT 5
#define VL_CTRL2 0x08
#define VL_SCALER_BYPASS_MODE BIT(0)
#define VL_STRIDE 0x0c
#define LUMA_STRIDE_SHIFT 16
#define LUMA_STRIDE_MASK (0xffff << LUMA_STRIDE_SHIFT)
#define CHROMA_STRIDE_SHIFT 0
#define CHROMA_STRIDE_MASK (0xffff << CHROMA_STRIDE_SHIFT)
#define VL_SRC_SIZE 0x10
#define VL_Y 0x14
#define VL_POS_START 0x30
#define VL_POS_END 0x34
#define LUMA_STRIDE(x) (((x) << LUMA_STRIDE_SHIFT) & LUMA_STRIDE_MASK)
#define CHROMA_STRIDE(x) (((x) << CHROMA_STRIDE_SHIFT) & CHROMA_STRIDE_MASK)
/* CSC registers */
#define CSC_CTRL0 0x30
#define CSC_COV_MODE_SHIFT 16
......@@ -69,6 +100,18 @@
#define RSZ_DEST_CFG 0x04
#define RSZ_ENABLE_CFG 0x14
#define RSZ_VL_LUMA_HOR 0x08
#define RSZ_VL_LUMA_VER 0x0c
#define RSZ_VL_CHROMA_HOR 0x10
#define RSZ_VL_CHROMA_VER 0x14
#define RSZ_VL_CTRL_CFG 0x18
#define RSZ_VL_FMT_SHIFT 3
#define RSZ_VL_FMT_MASK (0x3 << RSZ_VL_FMT_SHIFT)
#define RSZ_VL_FMT_YCBCR420 (0x0 << RSZ_VL_FMT_SHIFT)
#define RSZ_VL_FMT_YCBCR422 (0x1 << RSZ_VL_FMT_SHIFT)
#define RSZ_VL_FMT_YCBCR444 (0x2 << RSZ_VL_FMT_SHIFT)
#define RSZ_VL_ENABLE_CFG 0x1c
#define RSZ_VER_SHIFT 16
#define RSZ_VER_MASK (0xffff << RSZ_VER_SHIFT)
#define RSZ_HOR_SHIFT 0
......@@ -77,6 +120,14 @@
#define RSZ_VER(x) (((x) << RSZ_VER_SHIFT) & RSZ_VER_MASK)
#define RSZ_HOR(x) (((x) << RSZ_HOR_SHIFT) & RSZ_HOR_MASK)
#define RSZ_DATA_STEP_SHIFT 16
#define RSZ_DATA_STEP_MASK (0xffff << RSZ_DATA_STEP_SHIFT)
#define RSZ_PARA_STEP_SHIFT 0
#define RSZ_PARA_STEP_MASK (0xffff << RSZ_PARA_STEP_SHIFT)
#define RSZ_DATA_STEP(x) (((x) << RSZ_DATA_STEP_SHIFT) & RSZ_DATA_STEP_MASK)
#define RSZ_PARA_STEP(x) (((x) << RSZ_PARA_STEP_SHIFT) & RSZ_PARA_STEP_MASK)
/* HBSC registers */
#define HBSC_SATURATION 0x00
#define HBSC_HUE 0x04
......
/*
* Copyright 2017 Linaro Ltd.
* Copyright 2017 ZTE Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drmP.h>
#include "zx_drm_drv.h"
#include "zx_tvenc_regs.h"
#include "zx_vou.h"
struct zx_tvenc_pwrctrl {
struct regmap *regmap;
u32 reg;
u32 mask;
};
struct zx_tvenc {
struct drm_connector connector;
struct drm_encoder encoder;
struct device *dev;
void __iomem *mmio;
const struct vou_inf *inf;
struct zx_tvenc_pwrctrl pwrctrl;
};
#define to_zx_tvenc(x) container_of(x, struct zx_tvenc, x)
struct zx_tvenc_mode {
struct drm_display_mode mode;
u32 video_info;
u32 video_res;
u32 field1_param;
u32 field2_param;
u32 burst_line_odd1;
u32 burst_line_even1;
u32 burst_line_odd2;
u32 burst_line_even2;
u32 line_timing_param;
u32 weight_value;
u32 blank_black_level;
u32 burst_level;
u32 control_param;
u32 sub_carrier_phase1;
u32 phase_line_incr_cvbs;
};
/*
* The CRM cannot directly provide a suitable frequency, and we have to
* ask a multiplied rate from CRM and use the divider in VOU to get the
* desired one.
*/
#define TVENC_CLOCK_MULTIPLIER 4
static const struct zx_tvenc_mode tvenc_mode_pal = {
.mode = {
.clock = 13500 * TVENC_CLOCK_MULTIPLIER,
.hdisplay = 720,
.hsync_start = 720 + 12,
.hsync_end = 720 + 12 + 2,
.htotal = 720 + 12 + 2 + 130,
.vdisplay = 576,
.vsync_start = 576 + 2,
.vsync_end = 576 + 2 + 2,
.vtotal = 576 + 2 + 2 + 20,
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_INTERLACE,
},
.video_info = 0x00040040,
.video_res = 0x05a9c760,
.field1_param = 0x0004d416,
.field2_param = 0x0009b94f,
.burst_line_odd1 = 0x0004d406,
.burst_line_even1 = 0x0009b53e,
.burst_line_odd2 = 0x0004d805,
.burst_line_even2 = 0x0009b93f,
.line_timing_param = 0x06a96fdf,
.weight_value = 0x00c188a0,
.blank_black_level = 0x0000fcfc,
.burst_level = 0x00001595,
.control_param = 0x00000001,
.sub_carrier_phase1 = 0x1504c566,
.phase_line_incr_cvbs = 0xc068db8c,
};
static const struct zx_tvenc_mode tvenc_mode_ntsc = {
.mode = {
.clock = 13500 * TVENC_CLOCK_MULTIPLIER,
.hdisplay = 720,
.hsync_start = 720 + 16,
.hsync_end = 720 + 16 + 2,
.htotal = 720 + 16 + 2 + 120,
.vdisplay = 480,
.vsync_start = 480 + 3,
.vsync_end = 480 + 3 + 2,
.vtotal = 480 + 3 + 2 + 17,
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_INTERLACE,
},
.video_info = 0x00040080,
.video_res = 0x05a8375a,
.field1_param = 0x00041817,
.field2_param = 0x0008351e,
.burst_line_odd1 = 0x00041006,
.burst_line_even1 = 0x0008290d,
.burst_line_odd2 = 0x00000000,
.burst_line_even2 = 0x00000000,
.line_timing_param = 0x06a8ef9e,
.weight_value = 0x00b68197,
.blank_black_level = 0x0000f0f0,
.burst_level = 0x0000009c,
.control_param = 0x00000001,
.sub_carrier_phase1 = 0x10f83e10,
.phase_line_incr_cvbs = 0x80000000,
};
static const struct zx_tvenc_mode *tvenc_modes[] = {
&tvenc_mode_pal,
&tvenc_mode_ntsc,
};
static const struct zx_tvenc_mode *
zx_tvenc_find_zmode(struct drm_display_mode *mode)
{
int i;
for (i = 0; i < ARRAY_SIZE(tvenc_modes); i++) {
const struct zx_tvenc_mode *zmode = tvenc_modes[i];
if (drm_mode_equal(mode, &zmode->mode))
return zmode;
}
return NULL;
}
static void zx_tvenc_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adj_mode)
{
struct zx_tvenc *tvenc = to_zx_tvenc(encoder);
const struct zx_tvenc_mode *zmode;
struct vou_div_config configs[] = {
{ VOU_DIV_INF, VOU_DIV_4 },
{ VOU_DIV_TVENC, VOU_DIV_1 },
{ VOU_DIV_LAYER, VOU_DIV_2 },
};
zx_vou_config_dividers(encoder->crtc, configs, ARRAY_SIZE(configs));
zmode = zx_tvenc_find_zmode(mode);
if (!zmode) {
DRM_DEV_ERROR(tvenc->dev, "failed to find zmode\n");
return;
}
zx_writel(tvenc->mmio + VENC_VIDEO_INFO, zmode->video_info);
zx_writel(tvenc->mmio + VENC_VIDEO_RES, zmode->video_res);
zx_writel(tvenc->mmio + VENC_FIELD1_PARAM, zmode->field1_param);
zx_writel(tvenc->mmio + VENC_FIELD2_PARAM, zmode->field2_param);
zx_writel(tvenc->mmio + VENC_LINE_O_1, zmode->burst_line_odd1);
zx_writel(tvenc->mmio + VENC_LINE_E_1, zmode->burst_line_even1);
zx_writel(tvenc->mmio + VENC_LINE_O_2, zmode->burst_line_odd2);
zx_writel(tvenc->mmio + VENC_LINE_E_2, zmode->burst_line_even2);
zx_writel(tvenc->mmio + VENC_LINE_TIMING_PARAM,
zmode->line_timing_param);
zx_writel(tvenc->mmio + VENC_WEIGHT_VALUE, zmode->weight_value);
zx_writel(tvenc->mmio + VENC_BLANK_BLACK_LEVEL,
zmode->blank_black_level);
zx_writel(tvenc->mmio + VENC_BURST_LEVEL, zmode->burst_level);
zx_writel(tvenc->mmio + VENC_CONTROL_PARAM, zmode->control_param);
zx_writel(tvenc->mmio + VENC_SUB_CARRIER_PHASE1,
zmode->sub_carrier_phase1);
zx_writel(tvenc->mmio + VENC_PHASE_LINE_INCR_CVBS,
zmode->phase_line_incr_cvbs);
}
static void zx_tvenc_encoder_enable(struct drm_encoder *encoder)
{
struct zx_tvenc *tvenc = to_zx_tvenc(encoder);
struct zx_tvenc_pwrctrl *pwrctrl = &tvenc->pwrctrl;
/* Set bit to power up TVENC DAC */
regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask,
pwrctrl->mask);
vou_inf_enable(VOU_TV_ENC, encoder->crtc);
zx_writel(tvenc->mmio + VENC_ENABLE, 1);
}
static void zx_tvenc_encoder_disable(struct drm_encoder *encoder)
{
struct zx_tvenc *tvenc = to_zx_tvenc(encoder);
struct zx_tvenc_pwrctrl *pwrctrl = &tvenc->pwrctrl;
zx_writel(tvenc->mmio + VENC_ENABLE, 0);
vou_inf_disable(VOU_TV_ENC, encoder->crtc);
/* Clear bit to power down TVENC DAC */
regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask, 0);
}
static const struct drm_encoder_helper_funcs zx_tvenc_encoder_helper_funcs = {
.enable = zx_tvenc_encoder_enable,
.disable = zx_tvenc_encoder_disable,
.mode_set = zx_tvenc_encoder_mode_set,
};
static const struct drm_encoder_funcs zx_tvenc_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static int zx_tvenc_connector_get_modes(struct drm_connector *connector)
{
struct zx_tvenc *tvenc = to_zx_tvenc(connector);
struct device *dev = tvenc->dev;
int i;
for (i = 0; i < ARRAY_SIZE(tvenc_modes); i++) {
const struct zx_tvenc_mode *zmode = tvenc_modes[i];
struct drm_display_mode *mode;
mode = drm_mode_duplicate(connector->dev, &zmode->mode);
if (!mode) {
DRM_DEV_ERROR(dev, "failed to duplicate drm mode\n");
continue;
}
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode);
}
return i;
}
static enum drm_mode_status
zx_tvenc_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct zx_tvenc *tvenc = to_zx_tvenc(connector);
const struct zx_tvenc_mode *zmode;
zmode = zx_tvenc_find_zmode(mode);
if (!zmode) {
DRM_DEV_ERROR(tvenc->dev, "unsupported mode: %s\n", mode->name);
return MODE_NOMODE;
}
return MODE_OK;
}
static struct drm_connector_helper_funcs zx_tvenc_connector_helper_funcs = {
.get_modes = zx_tvenc_connector_get_modes,
.mode_valid = zx_tvenc_connector_mode_valid,
};
static const struct drm_connector_funcs zx_tvenc_connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int zx_tvenc_register(struct drm_device *drm, struct zx_tvenc *tvenc)
{
struct drm_encoder *encoder = &tvenc->encoder;
struct drm_connector *connector = &tvenc->connector;
/*
* The tvenc is designed to use aux channel, as there is a deflicker
* block for the channel.
*/
encoder->possible_crtcs = BIT(1);
drm_encoder_init(drm, encoder, &zx_tvenc_encoder_funcs,
DRM_MODE_ENCODER_TVDAC, NULL);
drm_encoder_helper_add(encoder, &zx_tvenc_encoder_helper_funcs);
connector->interlace_allowed = true;
drm_connector_init(drm, connector, &zx_tvenc_connector_funcs,
DRM_MODE_CONNECTOR_Composite);
drm_connector_helper_add(connector, &zx_tvenc_connector_helper_funcs);
drm_mode_connector_attach_encoder(connector, encoder);
return 0;
}
static int zx_tvenc_pwrctrl_init(struct zx_tvenc *tvenc)
{
struct zx_tvenc_pwrctrl *pwrctrl = &tvenc->pwrctrl;
struct device *dev = tvenc->dev;
struct of_phandle_args out_args;
struct regmap *regmap;
int ret;
ret = of_parse_phandle_with_fixed_args(dev->of_node,
"zte,tvenc-power-control", 2, 0, &out_args);
if (ret)
return ret;
regmap = syscon_node_to_regmap(out_args.np);
if (IS_ERR(regmap)) {
ret = PTR_ERR(regmap);
goto out;
}
pwrctrl->regmap = regmap;
pwrctrl->reg = out_args.args[0];
pwrctrl->mask = out_args.args[1];
out:
of_node_put(out_args.np);
return ret;
}
static int zx_tvenc_bind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm = data;
struct resource *res;
struct zx_tvenc *tvenc;
int ret;
tvenc = devm_kzalloc(dev, sizeof(*tvenc), GFP_KERNEL);
if (!tvenc)
return -ENOMEM;
tvenc->dev = dev;
dev_set_drvdata(dev, tvenc);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
tvenc->mmio = devm_ioremap_resource(dev, res);
if (IS_ERR(tvenc->mmio)) {
ret = PTR_ERR(tvenc->mmio);
DRM_DEV_ERROR(dev, "failed to remap tvenc region: %d\n", ret);
return ret;
}
ret = zx_tvenc_pwrctrl_init(tvenc);
if (ret) {
DRM_DEV_ERROR(dev, "failed to init power control: %d\n", ret);
return ret;
}
ret = zx_tvenc_register(drm, tvenc);
if (ret) {
DRM_DEV_ERROR(dev, "failed to register tvenc: %d\n", ret);
return ret;
}
return 0;
}
static void zx_tvenc_unbind(struct device *dev, struct device *master,
void *data)
{
/* Nothing to do */
}
static const struct component_ops zx_tvenc_component_ops = {
.bind = zx_tvenc_bind,
.unbind = zx_tvenc_unbind,
};
static int zx_tvenc_probe(struct platform_device *pdev)
{
return component_add(&pdev->dev, &zx_tvenc_component_ops);
}
static int zx_tvenc_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &zx_tvenc_component_ops);
return 0;
}
static const struct of_device_id zx_tvenc_of_match[] = {
{ .compatible = "zte,zx296718-tvenc", },
{ /* end */ },
};
MODULE_DEVICE_TABLE(of, zx_tvenc_of_match);
struct platform_driver zx_tvenc_driver = {
.probe = zx_tvenc_probe,
.remove = zx_tvenc_remove,
.driver = {
.name = "zx-tvenc",
.of_match_table = zx_tvenc_of_match,
},
};
/*
* Copyright 2017 Linaro Ltd.
* Copyright 2017 ZTE Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#ifndef __ZX_TVENC_REGS_H__
#define __ZX_TVENC_REGS_H__
#define VENC_VIDEO_INFO 0x04
#define VENC_VIDEO_RES 0x08
#define VENC_FIELD1_PARAM 0x10
#define VENC_FIELD2_PARAM 0x14
#define VENC_LINE_O_1 0x18
#define VENC_LINE_E_1 0x1c
#define VENC_LINE_O_2 0x20
#define VENC_LINE_E_2 0x24
#define VENC_LINE_TIMING_PARAM 0x28
#define VENC_WEIGHT_VALUE 0x2c
#define VENC_BLANK_BLACK_LEVEL 0x30
#define VENC_BURST_LEVEL 0x34
#define VENC_CONTROL_PARAM 0x3c
#define VENC_SUB_CARRIER_PHASE1 0x40
#define VENC_PHASE_LINE_INCR_CVBS 0x48
#define VENC_ENABLE 0xa8
#endif /* __ZX_TVENC_REGS_H__ */
......@@ -40,6 +40,7 @@ struct zx_crtc_regs {
u32 fir_active;
u32 fir_htiming;
u32 fir_vtiming;
u32 sec_vtiming;
u32 timing_shift;
u32 timing_pi_shift;
};
......@@ -48,6 +49,7 @@ static const struct zx_crtc_regs main_crtc_regs = {
.fir_active = FIR_MAIN_ACTIVE,
.fir_htiming = FIR_MAIN_H_TIMING,
.fir_vtiming = FIR_MAIN_V_TIMING,
.sec_vtiming = SEC_MAIN_V_TIMING,
.timing_shift = TIMING_MAIN_SHIFT,
.timing_pi_shift = TIMING_MAIN_PI_SHIFT,
};
......@@ -56,6 +58,7 @@ static const struct zx_crtc_regs aux_crtc_regs = {
.fir_active = FIR_AUX_ACTIVE,
.fir_htiming = FIR_AUX_H_TIMING,
.fir_vtiming = FIR_AUX_V_TIMING,
.sec_vtiming = SEC_AUX_V_TIMING,
.timing_shift = TIMING_AUX_SHIFT,
.timing_pi_shift = TIMING_AUX_PI_SHIFT,
};
......@@ -65,7 +68,17 @@ struct zx_crtc_bits {
u32 polarity_shift;
u32 int_frame_mask;
u32 tc_enable;
u32 gl_enable;
u32 sec_vactive_shift;
u32 sec_vactive_mask;
u32 interlace_select;
u32 pi_enable;
u32 div_vga_shift;
u32 div_pic_shift;
u32 div_tvenc_shift;
u32 div_hdmi_pnx_shift;
u32 div_hdmi_shift;
u32 div_inf_shift;
u32 div_layer_shift;
};
static const struct zx_crtc_bits main_crtc_bits = {
......@@ -73,7 +86,17 @@ static const struct zx_crtc_bits main_crtc_bits = {
.polarity_shift = MAIN_POL_SHIFT,
.int_frame_mask = TIMING_INT_MAIN_FRAME,
.tc_enable = MAIN_TC_EN,
.gl_enable = OSD_CTRL0_GL0_EN,
.sec_vactive_shift = SEC_VACT_MAIN_SHIFT,
.sec_vactive_mask = SEC_VACT_MAIN_MASK,
.interlace_select = MAIN_INTERLACE_SEL,
.pi_enable = MAIN_PI_EN,
.div_vga_shift = VGA_MAIN_DIV_SHIFT,
.div_pic_shift = PIC_MAIN_DIV_SHIFT,
.div_tvenc_shift = TVENC_MAIN_DIV_SHIFT,
.div_hdmi_pnx_shift = HDMI_MAIN_PNX_DIV_SHIFT,
.div_hdmi_shift = HDMI_MAIN_DIV_SHIFT,
.div_inf_shift = INF_MAIN_DIV_SHIFT,
.div_layer_shift = LAYER_MAIN_DIV_SHIFT,
};
static const struct zx_crtc_bits aux_crtc_bits = {
......@@ -81,7 +104,17 @@ static const struct zx_crtc_bits aux_crtc_bits = {
.polarity_shift = AUX_POL_SHIFT,
.int_frame_mask = TIMING_INT_AUX_FRAME,
.tc_enable = AUX_TC_EN,
.gl_enable = OSD_CTRL0_GL1_EN,
.sec_vactive_shift = SEC_VACT_AUX_SHIFT,
.sec_vactive_mask = SEC_VACT_AUX_MASK,
.interlace_select = AUX_INTERLACE_SEL,
.pi_enable = AUX_PI_EN,
.div_vga_shift = VGA_AUX_DIV_SHIFT,
.div_pic_shift = PIC_AUX_DIV_SHIFT,
.div_tvenc_shift = TVENC_AUX_DIV_SHIFT,
.div_hdmi_pnx_shift = HDMI_AUX_PNX_DIV_SHIFT,
.div_hdmi_shift = HDMI_AUX_DIV_SHIFT,
.div_inf_shift = INF_AUX_DIV_SHIFT,
.div_layer_shift = LAYER_AUX_DIV_SHIFT,
};
struct zx_crtc {
......@@ -97,6 +130,40 @@ struct zx_crtc {
#define to_zx_crtc(x) container_of(x, struct zx_crtc, crtc)
struct vou_layer_bits {
u32 enable;
u32 chnsel;
u32 clksel;
};
static const struct vou_layer_bits zx_gl_bits[GL_NUM] = {
{
.enable = OSD_CTRL0_GL0_EN,
.chnsel = OSD_CTRL0_GL0_SEL,
.clksel = VOU_CLK_GL0_SEL,
}, {
.enable = OSD_CTRL0_GL1_EN,
.chnsel = OSD_CTRL0_GL1_SEL,
.clksel = VOU_CLK_GL1_SEL,
},
};
static const struct vou_layer_bits zx_vl_bits[VL_NUM] = {
{
.enable = OSD_CTRL0_VL0_EN,
.chnsel = OSD_CTRL0_VL0_SEL,
.clksel = VOU_CLK_VL0_SEL,
}, {
.enable = OSD_CTRL0_VL1_EN,
.chnsel = OSD_CTRL0_VL1_SEL,
.clksel = VOU_CLK_VL1_SEL,
}, {
.enable = OSD_CTRL0_VL2_EN,
.chnsel = OSD_CTRL0_VL2_SEL,
.clksel = VOU_CLK_VL2_SEL,
},
};
struct zx_vou_hw {
struct device *dev;
void __iomem *osd;
......@@ -112,6 +179,33 @@ struct zx_vou_hw {
struct zx_crtc *aux_crtc;
};
enum vou_inf_data_sel {
VOU_YUV444 = 0,
VOU_RGB_101010 = 1,
VOU_RGB_888 = 2,
VOU_RGB_666 = 3,
};
struct vou_inf {
enum vou_inf_id id;
enum vou_inf_data_sel data_sel;
u32 clocks_en_bits;
u32 clocks_sel_bits;
};
static struct vou_inf vou_infs[] = {
[VOU_HDMI] = {
.data_sel = VOU_YUV444,
.clocks_en_bits = BIT(24) | BIT(18) | BIT(6),
.clocks_sel_bits = BIT(13) | BIT(2),
},
[VOU_TV_ENC] = {
.data_sel = VOU_YUV444,
.clocks_en_bits = BIT(15),
.clocks_sel_bits = BIT(11) | BIT(0),
},
};
static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
{
struct zx_crtc *zcrtc = to_zx_crtc(crtc);
......@@ -119,20 +213,30 @@ static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
return zcrtc->vou;
}
void vou_inf_enable(const struct vou_inf *inf, struct drm_crtc *crtc)
void vou_inf_hdmi_audio_sel(struct drm_crtc *crtc,
enum vou_inf_hdmi_audio aud)
{
struct zx_crtc *zcrtc = to_zx_crtc(crtc);
struct zx_vou_hw *vou = zcrtc->vou;
zx_writel_mask(vou->vouctl + VOU_INF_HDMI_CTRL, VOU_HDMI_AUD_MASK, aud);
}
void vou_inf_enable(enum vou_inf_id id, struct drm_crtc *crtc)
{
struct zx_crtc *zcrtc = to_zx_crtc(crtc);
struct zx_vou_hw *vou = zcrtc->vou;
struct vou_inf *inf = &vou_infs[id];
bool is_main = zcrtc->chn_type == VOU_CHN_MAIN;
u32 data_sel_shift = inf->id << 1;
u32 data_sel_shift = id << 1;
/* Select data format */
zx_writel_mask(vou->vouctl + VOU_INF_DATA_SEL, 0x3 << data_sel_shift,
inf->data_sel << data_sel_shift);
/* Select channel */
zx_writel_mask(vou->vouctl + VOU_INF_CH_SEL, 0x1 << inf->id,
zcrtc->chn_type << inf->id);
zx_writel_mask(vou->vouctl + VOU_INF_CH_SEL, 0x1 << id,
zcrtc->chn_type << id);
/* Select interface clocks */
zx_writel_mask(vou->vouctl + VOU_CLK_SEL, inf->clocks_sel_bits,
......@@ -143,20 +247,79 @@ void vou_inf_enable(const struct vou_inf *inf, struct drm_crtc *crtc)
inf->clocks_en_bits);
/* Enable the device */
zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << inf->id, 1 << inf->id);
zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << id, 1 << id);
}
void vou_inf_disable(const struct vou_inf *inf, struct drm_crtc *crtc)
void vou_inf_disable(enum vou_inf_id id, struct drm_crtc *crtc)
{
struct zx_vou_hw *vou = crtc_to_vou(crtc);
struct vou_inf *inf = &vou_infs[id];
/* Disable the device */
zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << inf->id, 0);
zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << id, 0);
/* Disable interface clocks */
zx_writel_mask(vou->vouctl + VOU_CLK_EN, inf->clocks_en_bits, 0);
}
void zx_vou_config_dividers(struct drm_crtc *crtc,
struct vou_div_config *configs, int num)
{
struct zx_crtc *zcrtc = to_zx_crtc(crtc);
struct zx_vou_hw *vou = zcrtc->vou;
const struct zx_crtc_bits *bits = zcrtc->bits;
int i;
/* Clear update flag bit */
zx_writel_mask(vou->vouctl + VOU_DIV_PARA, DIV_PARA_UPDATE, 0);
for (i = 0; i < num; i++) {
struct vou_div_config *cfg = configs + i;
u32 reg, shift;
switch (cfg->id) {
case VOU_DIV_VGA:
reg = VOU_CLK_SEL;
shift = bits->div_vga_shift;
break;
case VOU_DIV_PIC:
reg = VOU_CLK_SEL;
shift = bits->div_pic_shift;
break;
case VOU_DIV_TVENC:
reg = VOU_DIV_PARA;
shift = bits->div_tvenc_shift;
break;
case VOU_DIV_HDMI_PNX:
reg = VOU_DIV_PARA;
shift = bits->div_hdmi_pnx_shift;
break;
case VOU_DIV_HDMI:
reg = VOU_DIV_PARA;
shift = bits->div_hdmi_shift;
break;
case VOU_DIV_INF:
reg = VOU_DIV_PARA;
shift = bits->div_inf_shift;
break;
case VOU_DIV_LAYER:
reg = VOU_DIV_PARA;
shift = bits->div_layer_shift;
break;
default:
continue;
}
/* Each divider occupies 3 bits */
zx_writel_mask(vou->vouctl + reg, 0x7 << shift,
cfg->val << shift);
}
/* Set update flag bit to get dividers effected */
zx_writel_mask(vou->vouctl + VOU_DIV_PARA, DIV_PARA_UPDATE,
DIV_PARA_UPDATE);
}
static inline void vou_chn_set_update(struct zx_crtc *zcrtc)
{
zx_writel(zcrtc->chnreg + CHN_UPDATE, 1);
......@@ -165,11 +328,13 @@ static inline void vou_chn_set_update(struct zx_crtc *zcrtc)
static void zx_crtc_enable(struct drm_crtc *crtc)
{
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
struct zx_crtc *zcrtc = to_zx_crtc(crtc);
struct zx_vou_hw *vou = zcrtc->vou;
const struct zx_crtc_regs *regs = zcrtc->regs;
const struct zx_crtc_bits *bits = zcrtc->bits;
struct videomode vm;
u32 scan_mask;
u32 pol = 0;
u32 val;
int ret;
......@@ -177,7 +342,7 @@ static void zx_crtc_enable(struct drm_crtc *crtc)
drm_display_mode_to_videomode(mode, &vm);
/* Set up timing parameters */
val = V_ACTIVE(vm.vactive - 1);
val = V_ACTIVE((interlaced ? vm.vactive / 2 : vm.vactive) - 1);
val |= H_ACTIVE(vm.hactive - 1);
zx_writel(vou->timing + regs->fir_active, val);
......@@ -191,6 +356,25 @@ static void zx_crtc_enable(struct drm_crtc *crtc)
val |= FRONT_PORCH(vm.vfront_porch - 1);
zx_writel(vou->timing + regs->fir_vtiming, val);
if (interlaced) {
u32 shift = bits->sec_vactive_shift;
u32 mask = bits->sec_vactive_mask;
val = zx_readl(vou->timing + SEC_V_ACTIVE);
val &= ~mask;
val |= ((vm.vactive / 2 - 1) << shift) & mask;
zx_writel(vou->timing + SEC_V_ACTIVE, val);
val = SYNC_WIDE(vm.vsync_len - 1);
/*
* The vback_porch for the second field needs to shift one on
* the value for the first field.
*/
val |= BACK_PORCH(vm.vback_porch);
val |= FRONT_PORCH(vm.vfront_porch - 1);
zx_writel(vou->timing + regs->sec_vtiming, val);
}
/* Set up polarities */
if (vm.flags & DISPLAY_FLAGS_VSYNC_LOW)
pol |= 1 << POL_VSYNC_SHIFT;
......@@ -201,9 +385,17 @@ static void zx_crtc_enable(struct drm_crtc *crtc)
pol << bits->polarity_shift);
/* Setup SHIFT register by following what ZTE BSP does */
zx_writel(vou->timing + regs->timing_shift, H_SHIFT_VAL);
val = H_SHIFT_VAL;
if (interlaced)
val |= V_SHIFT_VAL << 16;
zx_writel(vou->timing + regs->timing_shift, val);
zx_writel(vou->timing + regs->timing_pi_shift, H_PI_SHIFT_VAL);
/* Progressive or interlace scan select */
scan_mask = bits->interlace_select | bits->pi_enable;
zx_writel_mask(vou->timing + SCAN_CTRL, scan_mask,
interlaced ? scan_mask : 0);
/* Enable TIMING_CTRL */
zx_writel_mask(vou->timing + TIMING_TC_ENABLE, bits->tc_enable,
bits->tc_enable);
......@@ -214,16 +406,16 @@ static void zx_crtc_enable(struct drm_crtc *crtc)
zx_writel_mask(zcrtc->chnreg + CHN_CTRL1, CHN_SCREEN_H_MASK,
vm.vactive << CHN_SCREEN_H_SHIFT);
/* Configure channel interlace buffer control */
zx_writel_mask(zcrtc->chnreg + CHN_INTERLACE_BUF_CTRL, CHN_INTERLACE_EN,
interlaced ? CHN_INTERLACE_EN : 0);
/* Update channel */
vou_chn_set_update(zcrtc);
/* Enable channel */
zx_writel_mask(zcrtc->chnreg + CHN_CTRL0, CHN_ENABLE, CHN_ENABLE);
/* Enable Graphic Layer */
zx_writel_mask(vou->osd + OSD_CTRL0, bits->gl_enable,
bits->gl_enable);
drm_crtc_vblank_on(crtc);
ret = clk_set_rate(zcrtc->pixclk, mode->clock * 1000);
......@@ -247,9 +439,6 @@ static void zx_crtc_disable(struct drm_crtc *crtc)
drm_crtc_vblank_off(crtc);
/* Disable Graphic Layer */
zx_writel_mask(vou->osd + OSD_CTRL0, bits->gl_enable, 0);
/* Disable channel */
zx_writel_mask(zcrtc->chnreg + CHN_CTRL0, CHN_ENABLE, 0);
......@@ -294,7 +483,7 @@ static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou,
enum vou_chn_type chn_type)
{
struct device *dev = vou->dev;
struct zx_layer_data data;
struct zx_plane *zplane;
struct zx_crtc *zcrtc;
int ret;
......@@ -305,19 +494,27 @@ static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou,
zcrtc->vou = vou;
zcrtc->chn_type = chn_type;
zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL);
if (!zplane)
return -ENOMEM;
zplane->dev = dev;
if (chn_type == VOU_CHN_MAIN) {
data.layer = vou->osd + MAIN_GL_OFFSET;
data.csc = vou->osd + MAIN_CSC_OFFSET;
data.hbsc = vou->osd + MAIN_HBSC_OFFSET;
data.rsz = vou->otfppu + MAIN_RSZ_OFFSET;
zplane->layer = vou->osd + MAIN_GL_OFFSET;
zplane->csc = vou->osd + MAIN_CSC_OFFSET;
zplane->hbsc = vou->osd + MAIN_HBSC_OFFSET;
zplane->rsz = vou->otfppu + MAIN_RSZ_OFFSET;
zplane->bits = &zx_gl_bits[0];
zcrtc->chnreg = vou->osd + OSD_MAIN_CHN;
zcrtc->regs = &main_crtc_regs;
zcrtc->bits = &main_crtc_bits;
} else {
data.layer = vou->osd + AUX_GL_OFFSET;
data.csc = vou->osd + AUX_CSC_OFFSET;
data.hbsc = vou->osd + AUX_HBSC_OFFSET;
data.rsz = vou->otfppu + AUX_RSZ_OFFSET;
zplane->layer = vou->osd + AUX_GL_OFFSET;
zplane->csc = vou->osd + AUX_CSC_OFFSET;
zplane->hbsc = vou->osd + AUX_HBSC_OFFSET;
zplane->rsz = vou->otfppu + AUX_RSZ_OFFSET;
zplane->bits = &zx_gl_bits[1];
zcrtc->chnreg = vou->osd + OSD_AUX_CHN;
zcrtc->regs = &aux_crtc_regs;
zcrtc->bits = &aux_crtc_bits;
......@@ -331,13 +528,14 @@ static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou,
return ret;
}
zcrtc->primary = zx_plane_init(drm, dev, &data, DRM_PLANE_TYPE_PRIMARY);
if (IS_ERR(zcrtc->primary)) {
ret = PTR_ERR(zcrtc->primary);
ret = zx_plane_init(drm, zplane, DRM_PLANE_TYPE_PRIMARY);
if (ret) {
DRM_DEV_ERROR(dev, "failed to init primary plane: %d\n", ret);
return ret;
}
zcrtc->primary = &zplane->plane;
ret = drm_crtc_init_with_planes(drm, &zcrtc->crtc, zcrtc->primary, NULL,
&zx_crtc_funcs, NULL);
if (ret) {
......@@ -393,6 +591,78 @@ void zx_vou_disable_vblank(struct drm_device *drm, unsigned int pipe)
zcrtc->bits->int_frame_mask, 0);
}
void zx_vou_layer_enable(struct drm_plane *plane)
{
struct zx_crtc *zcrtc = to_zx_crtc(plane->state->crtc);
struct zx_vou_hw *vou = zcrtc->vou;
struct zx_plane *zplane = to_zx_plane(plane);
const struct vou_layer_bits *bits = zplane->bits;
if (zcrtc->chn_type == VOU_CHN_MAIN) {
zx_writel_mask(vou->osd + OSD_CTRL0, bits->chnsel, 0);
zx_writel_mask(vou->vouctl + VOU_CLK_SEL, bits->clksel, 0);
} else {
zx_writel_mask(vou->osd + OSD_CTRL0, bits->chnsel,
bits->chnsel);
zx_writel_mask(vou->vouctl + VOU_CLK_SEL, bits->clksel,
bits->clksel);
}
zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, bits->enable);
}
void zx_vou_layer_disable(struct drm_plane *plane)
{
struct zx_crtc *zcrtc = to_zx_crtc(plane->crtc);
struct zx_vou_hw *vou = zcrtc->vou;
struct zx_plane *zplane = to_zx_plane(plane);
const struct vou_layer_bits *bits = zplane->bits;
zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, 0);
}
static void zx_overlay_init(struct drm_device *drm, struct zx_vou_hw *vou)
{
struct device *dev = vou->dev;
struct zx_plane *zplane;
int i;
int ret;
/*
* VL0 has some quirks on scaling support which need special handling.
* Let's leave it out for now.
*/
for (i = 1; i < VL_NUM; i++) {
zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL);
if (!zplane) {
DRM_DEV_ERROR(dev, "failed to allocate zplane %d\n", i);
return;
}
zplane->layer = vou->osd + OSD_VL_OFFSET(i);
zplane->hbsc = vou->osd + HBSC_VL_OFFSET(i);
zplane->rsz = vou->otfppu + RSZ_VL_OFFSET(i);
zplane->bits = &zx_vl_bits[i];
ret = zx_plane_init(drm, zplane, DRM_PLANE_TYPE_OVERLAY);
if (ret) {
DRM_DEV_ERROR(dev, "failed to init overlay %d\n", i);
continue;
}
}
}
static inline void zx_osd_int_update(struct zx_crtc *zcrtc)
{
struct drm_crtc *crtc = &zcrtc->crtc;
struct drm_plane *plane;
vou_chn_set_update(zcrtc);
drm_for_each_plane_mask(plane, crtc->dev, crtc->state->plane_mask)
zx_plane_set_update(plane);
}
static irqreturn_t vou_irq_handler(int irq, void *dev_id)
{
struct zx_vou_hw *vou = dev_id;
......@@ -412,15 +682,11 @@ static irqreturn_t vou_irq_handler(int irq, void *dev_id)
state = zx_readl(vou->osd + OSD_INT_STA);
zx_writel(vou->osd + OSD_INT_CLRSTA, state);
if (state & OSD_INT_MAIN_UPT) {
vou_chn_set_update(vou->main_crtc);
zx_plane_set_update(vou->main_crtc->primary);
}
if (state & OSD_INT_MAIN_UPT)
zx_osd_int_update(vou->main_crtc);
if (state & OSD_INT_AUX_UPT) {
vou_chn_set_update(vou->aux_crtc);
zx_plane_set_update(vou->aux_crtc->primary);
}
if (state & OSD_INT_AUX_UPT)
zx_osd_int_update(vou->aux_crtc);
if (state & OSD_INT_ERROR)
DRM_DEV_ERROR(vou->dev, "OSD ERROR: 0x%08x!\n", state);
......@@ -451,19 +717,9 @@ static void vou_dtrc_init(struct zx_vou_hw *vou)
static void vou_hw_init(struct zx_vou_hw *vou)
{
/* Set GL0 to main channel and GL1 to aux channel */
zx_writel_mask(vou->osd + OSD_CTRL0, OSD_CTRL0_GL0_SEL, 0);
zx_writel_mask(vou->osd + OSD_CTRL0, OSD_CTRL0_GL1_SEL,
OSD_CTRL0_GL1_SEL);
/* Release reset for all VOU modules */
zx_writel(vou->vouctl + VOU_SOFT_RST, ~0);
/* Select main clock for GL0 and aux clock for GL1 module */
zx_writel_mask(vou->vouctl + VOU_CLK_SEL, VOU_CLK_GL0_SEL, 0);
zx_writel_mask(vou->vouctl + VOU_CLK_SEL, VOU_CLK_GL1_SEL,
VOU_CLK_GL1_SEL);
/* Enable clock auto-gating for all VOU modules */
zx_writel(vou->vouctl + VOU_CLK_REQEN, ~0);
......@@ -600,6 +856,8 @@ static int zx_crtc_bind(struct device *dev, struct device *master, void *data)
goto disable_ppu_clk;
}
zx_overlay_init(drm, vou);
return 0;
disable_ppu_clk:
......
......@@ -23,24 +23,48 @@ enum vou_inf_id {
VOU_VGA = 5,
};
enum vou_inf_data_sel {
VOU_YUV444 = 0,
VOU_RGB_101010 = 1,
VOU_RGB_888 = 2,
VOU_RGB_666 = 3,
enum vou_inf_hdmi_audio {
VOU_HDMI_AUD_SPDIF = BIT(0),
VOU_HDMI_AUD_I2S = BIT(1),
VOU_HDMI_AUD_DSD = BIT(2),
VOU_HDMI_AUD_HBR = BIT(3),
VOU_HDMI_AUD_PARALLEL = BIT(4),
};
struct vou_inf {
enum vou_inf_id id;
enum vou_inf_data_sel data_sel;
u32 clocks_en_bits;
u32 clocks_sel_bits;
void vou_inf_hdmi_audio_sel(struct drm_crtc *crtc,
enum vou_inf_hdmi_audio aud);
void vou_inf_enable(enum vou_inf_id id, struct drm_crtc *crtc);
void vou_inf_disable(enum vou_inf_id id, struct drm_crtc *crtc);
enum vou_div_id {
VOU_DIV_VGA,
VOU_DIV_PIC,
VOU_DIV_TVENC,
VOU_DIV_HDMI_PNX,
VOU_DIV_HDMI,
VOU_DIV_INF,
VOU_DIV_LAYER,
};
enum vou_div_val {
VOU_DIV_1 = 0,
VOU_DIV_2 = 1,
VOU_DIV_4 = 3,
VOU_DIV_8 = 7,
};
void vou_inf_enable(const struct vou_inf *inf, struct drm_crtc *crtc);
void vou_inf_disable(const struct vou_inf *inf, struct drm_crtc *crtc);
struct vou_div_config {
enum vou_div_id id;
enum vou_div_val val;
};
void zx_vou_config_dividers(struct drm_crtc *crtc,
struct vou_div_config *configs, int num);
int zx_vou_enable_vblank(struct drm_device *drm, unsigned int pipe);
void zx_vou_disable_vblank(struct drm_device *drm, unsigned int pipe);
void zx_vou_layer_enable(struct drm_plane *plane);
void zx_vou_layer_disable(struct drm_plane *plane);
#endif /* __ZX_VOU_H__ */
......@@ -22,6 +22,15 @@
#define AUX_HBSC_OFFSET 0x860
#define AUX_RSZ_OFFSET 0x800
#define OSD_VL0_OFFSET 0x040
#define OSD_VL_OFFSET(i) (OSD_VL0_OFFSET + 0x050 * (i))
#define HBSC_VL0_OFFSET 0x760
#define HBSC_VL_OFFSET(i) (HBSC_VL0_OFFSET + 0x040 * (i))
#define RSZ_VL1_U0 0xa00
#define RSZ_VL_OFFSET(i) (RSZ_VL1_U0 + 0x200 * (i))
/* OSD (GPC_GLOBAL) registers */
#define OSD_INT_STA 0x04
#define OSD_INT_CLRSTA 0x08
......@@ -42,6 +51,12 @@
)
#define OSD_INT_ENABLE (OSD_INT_ERROR | OSD_INT_AUX_UPT | OSD_INT_MAIN_UPT)
#define OSD_CTRL0 0x10
#define OSD_CTRL0_VL0_EN BIT(13)
#define OSD_CTRL0_VL0_SEL BIT(12)
#define OSD_CTRL0_VL1_EN BIT(11)
#define OSD_CTRL0_VL1_SEL BIT(10)
#define OSD_CTRL0_VL2_EN BIT(9)
#define OSD_CTRL0_VL2_SEL BIT(8)
#define OSD_CTRL0_GL0_EN BIT(7)
#define OSD_CTRL0_GL0_SEL BIT(6)
#define OSD_CTRL0_GL1_EN BIT(5)
......@@ -60,6 +75,8 @@
#define CHN_SCREEN_H_SHIFT 5
#define CHN_SCREEN_H_MASK (0x1fff << CHN_SCREEN_H_SHIFT)
#define CHN_UPDATE 0x08
#define CHN_INTERLACE_BUF_CTRL 0x24
#define CHN_INTERLACE_EN BIT(2)
/* TIMING_CTRL registers */
#define TIMING_TC_ENABLE 0x04
......@@ -102,6 +119,19 @@
#define TIMING_MAIN_SHIFT 0x2c
#define TIMING_AUX_SHIFT 0x30
#define H_SHIFT_VAL 0x0048
#define V_SHIFT_VAL 0x0001
#define SCAN_CTRL 0x34
#define AUX_PI_EN BIT(19)
#define MAIN_PI_EN BIT(18)
#define AUX_INTERLACE_SEL BIT(1)
#define MAIN_INTERLACE_SEL BIT(0)
#define SEC_V_ACTIVE 0x38
#define SEC_VACT_MAIN_SHIFT 0
#define SEC_VACT_MAIN_MASK (0xffff << SEC_VACT_MAIN_SHIFT)
#define SEC_VACT_AUX_SHIFT 16
#define SEC_VACT_AUX_MASK (0xffff << SEC_VACT_AUX_SHIFT)
#define SEC_MAIN_V_TIMING 0x3c
#define SEC_AUX_V_TIMING 0x40
#define TIMING_MAIN_PI_SHIFT 0x68
#define TIMING_AUX_PI_SHIFT 0x6c
#define H_PI_SHIFT_VAL 0x000f
......@@ -146,10 +176,31 @@
#define VOU_INF_DATA_SEL 0x08
#define VOU_SOFT_RST 0x14
#define VOU_CLK_SEL 0x18
#define VGA_AUX_DIV_SHIFT 29
#define VGA_MAIN_DIV_SHIFT 26
#define PIC_MAIN_DIV_SHIFT 23
#define PIC_AUX_DIV_SHIFT 20
#define VOU_CLK_VL2_SEL BIT(8)
#define VOU_CLK_VL1_SEL BIT(7)
#define VOU_CLK_VL0_SEL BIT(6)
#define VOU_CLK_GL1_SEL BIT(5)
#define VOU_CLK_GL0_SEL BIT(4)
#define VOU_DIV_PARA 0x1c
#define DIV_PARA_UPDATE BIT(31)
#define TVENC_AUX_DIV_SHIFT 28
#define HDMI_AUX_PNX_DIV_SHIFT 25
#define HDMI_MAIN_PNX_DIV_SHIFT 22
#define HDMI_AUX_DIV_SHIFT 19
#define HDMI_MAIN_DIV_SHIFT 16
#define TVENC_MAIN_DIV_SHIFT 13
#define INF_AUX_DIV_SHIFT 9
#define INF_MAIN_DIV_SHIFT 6
#define LAYER_AUX_DIV_SHIFT 3
#define LAYER_MAIN_DIV_SHIFT 0
#define VOU_CLK_REQEN 0x20
#define VOU_CLK_EN 0x24
#define VOU_INF_HDMI_CTRL 0x30
#define VOU_HDMI_AUD_MASK 0x1f
/* OTFPPU_CTRL registers */
#define OTFPPU_RSZ_DATA_SOURCE 0x04
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册