599 lines
20 KiB
C
599 lines
20 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2025 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/amlogic/media/vout/meson_tx_connector/meson_tx_format_para.h>
|
|
|
|
#include "meson_venc.h"
|
|
#include "meson_drm_main.h"
|
|
#include "vpu-hw/meson_vpu_reg.h"
|
|
|
|
#define DEVICE_NAME "meson_tx_venc"
|
|
#define MAX_VENC 4
|
|
|
|
struct regmap {
|
|
phys_addr_t phys_addr;
|
|
size_t size;
|
|
void __iomem *virt_addr;
|
|
};
|
|
|
|
struct meson_tx_venc {
|
|
struct platform_device *pdev;
|
|
struct regmap *regmap[MAX_VENC];
|
|
u32 venc_offset[MAX_VENC];
|
|
int num_regmap;
|
|
};
|
|
|
|
static struct regmap *regmap_init_mmio(phys_addr_t phys_addr, void __iomem *virt_addr, size_t size)
|
|
{
|
|
struct regmap *reg_map;
|
|
|
|
reg_map = kzalloc(sizeof(*reg_map), GFP_KERNEL);
|
|
if (!reg_map)
|
|
return NULL;
|
|
|
|
reg_map->phys_addr = phys_addr;
|
|
reg_map->size = size;
|
|
reg_map->virt_addr = virt_addr;
|
|
|
|
return reg_map;
|
|
}
|
|
|
|
static void regmap_write(struct regmap *regmap, unsigned int reg, unsigned int val)
|
|
{
|
|
unsigned int reg_offset = reg << 2;
|
|
void __iomem *p = regmap->virt_addr + reg_offset;
|
|
|
|
writel(val, p);
|
|
}
|
|
|
|
static void regmap_update_bits(struct regmap *regmap, unsigned int reg, unsigned int mask,
|
|
unsigned int val)
|
|
{
|
|
unsigned int reg_val;
|
|
unsigned int new_val;
|
|
unsigned int reg_offset = reg << 2;
|
|
void __iomem *p = regmap->virt_addr + reg_offset;
|
|
|
|
reg_val = readl(p);
|
|
new_val = (reg_val & ~mask) | (val & mask);
|
|
|
|
regmap_write(regmap, reg, new_val);
|
|
}
|
|
|
|
static void regmap_read(struct regmap *regmap, unsigned int reg, unsigned int *val)
|
|
{
|
|
unsigned int reg_offset = reg << 2;
|
|
void __iomem *p = regmap->virt_addr + reg_offset;
|
|
|
|
*val = readl(p);
|
|
}
|
|
|
|
int _meson_venc_bist_mode_set(struct regmap *regmap, enum venc_type enc_type,
|
|
enum venc_bist_type bist_type)
|
|
{
|
|
/* Both encp and encl have VIDEO_TST_EN, VIDEO_TST_MDSEL, ..., VIDEO_TST_VDCNT_STSET,
|
|
* only with the offset 0x128
|
|
*/
|
|
u32 encl_offset;
|
|
u32 reg_mode_adv;
|
|
u32 reg_rgbin_ctrl;
|
|
u32 h_active;
|
|
u32 v_active;
|
|
u32 h_start;
|
|
u32 temp;
|
|
|
|
if (!regmap)
|
|
return -1;
|
|
|
|
pr_info("%s enc_type[%s] bist_type[%d]\n",
|
|
__func__, enc_type == VENC_ENCL ? "ENCL" : "ENCP", bist_type);
|
|
|
|
encl_offset = (enc_type == VENC_ENCL) ? 0x0128 : 0;
|
|
reg_mode_adv = (enc_type == VENC_ENCL) ? ENCL_VIDEO_MODE_ADV : ENCP_VIDEO_MODE_ADV;
|
|
reg_rgbin_ctrl = (enc_type == VENC_ENCL) ? ENCL_VIDEO_RGBIN_CTRL : ENCP_VIDEO_RGBIN_CTRL;
|
|
|
|
if (bist_type == VENC_BIST_PTTN_OFF) {
|
|
regmap_update_bits(regmap, reg_mode_adv, BIT(3), BIT(3));
|
|
regmap_update_bits(regmap, VENC_VIDEO_TST_EN + encl_offset, BIT(0), 0);
|
|
return 0;
|
|
}
|
|
regmap_update_bits(regmap, VENC_VIDEO_TST_EN + encl_offset, GENMASK(2, 0), GENMASK(2, 0));
|
|
regmap_update_bits(regmap, reg_mode_adv, BIT(3), 0);
|
|
|
|
if (enc_type == VENC_ENCL) {
|
|
/* ENCL pure color pattern with RGB mode */
|
|
if (bist_type == VENC_BIST_PTTN_BLACK) {
|
|
regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0);
|
|
return 0;
|
|
}
|
|
if (bist_type == VENC_BIST_PTTN_WHITE) {
|
|
regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x3ff);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x3ff);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x3ff);
|
|
regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0);
|
|
return 0;
|
|
}
|
|
if (bist_type == VENC_BIST_PTTN_RED) {
|
|
regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x3ff);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0);
|
|
return 0;
|
|
}
|
|
if (bist_type == VENC_BIST_PTTN_GREEN) {
|
|
regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x3ff);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0);
|
|
return 0;
|
|
}
|
|
if (bist_type == VENC_BIST_PTTN_BLUE) {
|
|
regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x3ff);
|
|
regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0);
|
|
return 0;
|
|
}
|
|
/* for other patterns */
|
|
regmap_read(regmap, ENCL_VIDEO_HAVON_END, &temp);
|
|
regmap_read(regmap, ENCL_VIDEO_HAVON_BEGIN, &h_active);
|
|
/* note that need to use accurate h/vactive(1920/1080) instead
|
|
* of 1919/1079 in gray/cross pattern
|
|
*/
|
|
h_active = temp - h_active + 1;
|
|
regmap_read(regmap, ENCL_VIDEO_VAVON_ELINE, &temp);
|
|
regmap_read(regmap, ENCL_VIDEO_VAVON_BLINE, &v_active);
|
|
v_active = temp - v_active + 1;
|
|
regmap_read(regmap, ENCL_VIDEO_HAVON_BEGIN, &h_start);
|
|
} else if (enc_type == VENC_ENCP) {
|
|
/* ENCP pure color pattern with YCC mode */
|
|
if (bist_type == VENC_BIST_PTTN_BLACK) {
|
|
regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x200);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x200);
|
|
regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0);
|
|
return 0;
|
|
}
|
|
if (bist_type == VENC_BIST_PTTN_WHITE) {
|
|
regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x3ff);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x200);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x200);
|
|
regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0);
|
|
return 0;
|
|
}
|
|
if (bist_type == VENC_BIST_PTTN_RED) {
|
|
regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x200);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x3ff);
|
|
regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0);
|
|
return 0;
|
|
}
|
|
if (bist_type == VENC_BIST_PTTN_GREEN) {
|
|
regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x200);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0);
|
|
return 0;
|
|
}
|
|
if (bist_type == VENC_BIST_PTTN_BLUE) {
|
|
regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x200);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x3ff);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0);
|
|
return 0;
|
|
}
|
|
/* for other patterns */
|
|
regmap_read(regmap, ENCP_DE_H_END, &temp);
|
|
regmap_read(regmap, ENCP_DE_H_BEGIN, &h_active);
|
|
h_active = temp - h_active;
|
|
regmap_read(regmap, ENCP_DE_V_END_EVEN, &temp);
|
|
regmap_read(regmap, ENCP_DE_V_BEGIN_EVEN, &v_active);
|
|
v_active = temp - v_active;
|
|
regmap_read(regmap, ENCP_VIDEO_HAVON_BEGIN, &h_start);
|
|
}
|
|
|
|
if (bist_type == VENC_BIST_PTTN_LINE) {
|
|
regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 2);
|
|
return 0;
|
|
}
|
|
if (bist_type == VENC_BIST_PTTN_DOT) {
|
|
regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 3);
|
|
return 0;
|
|
}
|
|
if (bist_type == VENC_BIST_PTTN_COLORBAR) {
|
|
regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 1);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_STRT + encl_offset, h_start - 2);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_WIDTH + encl_offset, h_active / 8);
|
|
return 0;
|
|
}
|
|
if (bist_type == VENC_BIST_PTTN_CROSSING) {
|
|
regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 4);
|
|
regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x3ff);
|
|
/* aspect ratio: 16:9 */
|
|
regmap_update_bits(regmap, VENC_VIDEO_TST_CB + encl_offset, GENMASK(5, 0), 16);
|
|
regmap_update_bits(regmap, VENC_VIDEO_TST_CR + encl_offset, GENMASK(5, 0), 9);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_WIDTH + encl_offset, h_active);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_STRT + encl_offset, v_active);
|
|
/* cross box width: value 0 means default width = 1 */
|
|
regmap_write(regmap, VENC_VIDEO_TST_VDCNT_STSET + encl_offset, 0);
|
|
regmap_update_bits(regmap, reg_rgbin_ctrl, BIT(1), BIT(1));
|
|
/* when TST_MDSEL is 4, need to reset BIT(9) */
|
|
regmap_update_bits(regmap, VENC_VIDEO_TST_CB + encl_offset, BIT(9), BIT(9));
|
|
regmap_update_bits(regmap, VENC_VIDEO_TST_CB + encl_offset, BIT(9), 0);
|
|
return 0;
|
|
}
|
|
if (bist_type == VENC_BIST_PTTN_GRAY) {
|
|
u32 steps = 32;
|
|
|
|
regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 5);
|
|
regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 1024 / steps - 1);
|
|
regmap_update_bits(regmap, VENC_VIDEO_TST_CB + encl_offset, GENMASK(2, 0), 0);
|
|
regmap_update_bits(regmap, VENC_VIDEO_TST_CR + encl_offset, GENMASK(2, 0),
|
|
GENMASK(2, 0));
|
|
regmap_update_bits(regmap, VENC_VIDEO_TST_CR + encl_offset, GENMASK(9, 3),
|
|
(h_active / steps - 1) << 3);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_WIDTH + encl_offset, 0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_STRT + encl_offset, h_start - 2);
|
|
regmap_write(regmap, VENC_VIDEO_TST_VDCNT_STSET + encl_offset, 0);
|
|
regmap_update_bits(regmap, reg_rgbin_ctrl, BIT(1), BIT(1));
|
|
regmap_update_bits(regmap, VENC_VIDEO_TST_CB + encl_offset, BIT(9), 0);
|
|
return 0;
|
|
}
|
|
if (bist_type == VENC_BIST_PTTN_WINDOW) {
|
|
u32 startx, starty, endx, endy;
|
|
|
|
starty = v_active * 2 / 10; // 2/10 border
|
|
startx = h_active * 3 / 10; // 3/10 border
|
|
endy = v_active * 6 / 10; // 4/10 border
|
|
endx = h_active * 5 / 10; // 5/10 border
|
|
|
|
regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 6);
|
|
regmap_update_bits(regmap, reg_rgbin_ctrl, BIT(1), BIT(1));
|
|
// we shall set BIT7 to enable axis config latch
|
|
regmap_update_bits(regmap, VENC_VIDEO_TST_EN + encl_offset, BIT(7), BIT(7));
|
|
regmap_write(regmap, VENC_VIDEO_TST_VDCNT_STSET + encl_offset, 2);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_WIDTH + encl_offset, starty);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_STRT + encl_offset, startx);
|
|
regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x3ff);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x3ff);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_VDCNT_STSET + encl_offset, 2);
|
|
regmap_write(regmap, VENC_VIDEO_TST_VDCNT_STSET + encl_offset, 3);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_WIDTH + encl_offset, endy);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_STRT + encl_offset, endx);
|
|
regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x3ff);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x3ff);
|
|
regmap_write(regmap, VENC_VIDEO_TST_VDCNT_STSET + encl_offset, 3);
|
|
regmap_write(regmap, VENC_VIDEO_TST_VDCNT_STSET + encl_offset, 0);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_WIDTH + encl_offset, h_active);
|
|
regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_STRT + encl_offset, v_active);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void config_tv_encp_calc(struct regmap *regmap, struct meson_tx_format_para *para,
|
|
enum venc_bist_type bist_type)
|
|
{
|
|
const struct tx_timing *tp = NULL;
|
|
struct tx_timing timing = {0};
|
|
/* adjust to align upsample and video enable */
|
|
u32 hsync_st = 5; // hsync start pixel count
|
|
u32 vsync_st = 1; // vsync start line count
|
|
// Latency in pixel clock from ENCP_VFIFO2VD request to data ready to HDMI
|
|
const u32 vfifo2vd_to_hdmi_latency = 2;
|
|
u32 de_h_begin = 0;
|
|
u32 de_h_end = 0;
|
|
u32 de_v_begin = 0;
|
|
u32 de_v_end = 0;
|
|
bool y420_mode = 0;
|
|
int hpara_div = 1;
|
|
|
|
if (para->cs == HDMI_COLORSPACE_YUV420)
|
|
y420_mode = 1;
|
|
|
|
tp = ¶->timing;
|
|
timing = *tp;
|
|
|
|
timing.h_total /= hpara_div;
|
|
timing.h_blank /= hpara_div;
|
|
timing.h_front /= hpara_div;
|
|
timing.h_sync /= hpara_div;
|
|
timing.h_back /= hpara_div;
|
|
timing.h_active /= hpara_div;
|
|
|
|
de_h_end = tp->h_total - (tp->h_front - hsync_st);
|
|
de_h_begin = de_h_end - tp->h_active;
|
|
de_v_end = tp->v_total - (tp->v_front - vsync_st);
|
|
de_v_begin = de_v_end - tp->v_active;
|
|
|
|
// VENC timing gen is disabled
|
|
regmap_write(regmap, ENCP_VIDEO_EN, 0);
|
|
|
|
// Enable viu vsync interrupt
|
|
regmap_write(regmap, VPU_VENC_CTRL, 1);
|
|
|
|
// set DVI/HDMI transfer timing
|
|
// generate hsync
|
|
regmap_write(regmap, ENCP_DVI_HSO_BEGIN, hsync_st);
|
|
regmap_write(regmap, ENCP_DVI_HSO_END, hsync_st + tp->h_sync);
|
|
|
|
// generate vsync
|
|
regmap_write(regmap, ENCP_DVI_VSO_BLINE_EVN, vsync_st + y420_mode);
|
|
regmap_write(regmap, ENCP_DVI_VSO_ELINE_EVN,
|
|
vsync_st + tp->v_sync + y420_mode);
|
|
regmap_write(regmap, ENCP_DVI_VSO_BEGIN_EVN, hsync_st);
|
|
regmap_write(regmap, ENCP_DVI_VSO_END_EVN, hsync_st);
|
|
|
|
// generate data valid
|
|
regmap_write(regmap, ENCP_DE_H_BEGIN, de_h_begin);
|
|
regmap_write(regmap, ENCP_DE_H_END, de_h_end);
|
|
regmap_write(regmap, ENCP_DE_V_BEGIN_EVEN, de_v_begin);
|
|
/*
|
|
* The counting range is 0 ~ vtotal-1. When v_front_porch <= vsync start,
|
|
* the vactive_end needs to be processed according to vactive_end % vtotal.
|
|
*/
|
|
if (tp->v_front <= vsync_st)
|
|
regmap_write(regmap, ENCP_DE_V_END_EVEN, de_v_end % tp->v_total);
|
|
else
|
|
regmap_write(regmap, ENCP_DE_V_END_EVEN, de_v_end);
|
|
|
|
// set mode
|
|
// Enable Hsync and equalization pulse switch in center; bit[14] cfg_de_v = 1
|
|
regmap_write(regmap, ENCP_VIDEO_MODE, 0x0040 | (1 << 14));
|
|
regmap_write(regmap, ENCP_VIDEO_MODE_ADV, 0x18); // Sampling rate: 1
|
|
|
|
// set active region
|
|
regmap_write(regmap, ENCP_VIDEO_HAVON_BEGIN, de_h_begin - vfifo2vd_to_hdmi_latency);
|
|
regmap_write(regmap, ENCP_VIDEO_HAVON_END, de_h_end - vfifo2vd_to_hdmi_latency - 1);
|
|
regmap_write(regmap, ENCP_VIDEO_VAVON_BLINE, de_v_begin);
|
|
regmap_write(regmap, ENCP_VIDEO_VAVON_ELINE, de_v_end - 1);
|
|
|
|
//set hsync
|
|
regmap_write(regmap, ENCP_VIDEO_HSO_BEGIN, hsync_st - vfifo2vd_to_hdmi_latency);
|
|
regmap_write(regmap, ENCP_VIDEO_HSO_END, hsync_st + tp->h_sync - vfifo2vd_to_hdmi_latency);
|
|
|
|
//set vsync
|
|
regmap_write(regmap, ENCP_VIDEO_VSO_BEGIN, 0);
|
|
regmap_write(regmap, ENCP_VIDEO_VSO_END, 0);
|
|
regmap_write(regmap, ENCP_VIDEO_VSO_BLINE, vsync_st);
|
|
regmap_write(regmap, ENCP_VIDEO_VSO_ELINE, vsync_st + tp->v_sync);
|
|
|
|
//set vtotal & htotal
|
|
regmap_write(regmap, ENCP_VIDEO_MAX_PXCNT, tp->h_total - 1);
|
|
regmap_write(regmap, ENCP_VIDEO_MAX_LNCNT, tp->v_total - 1);
|
|
|
|
_meson_venc_bist_mode_set(regmap, VENC_ENCP, bist_type);
|
|
|
|
regmap_write(regmap, ENCP_VIDEO_EN, 1);
|
|
|
|
pr_info("%s set done\n", __func__);
|
|
}
|
|
|
|
static void config_tv_encl_calc(struct regmap *regmap, struct meson_tx_format_para *para,
|
|
enum venc_bist_type bist_type)
|
|
{
|
|
const struct tx_timing *tp = NULL;
|
|
u32 de_h_begin = 0;
|
|
u32 de_h_end = 0;
|
|
u32 de_v_begin = 0;
|
|
u32 de_v_end = 0;
|
|
|
|
tp = ¶->timing;
|
|
de_h_begin = tp->h_total - tp->h_front - tp->h_active;
|
|
de_h_end = tp->h_total - tp->h_front - 1;
|
|
de_v_begin = tp->v_total - tp->v_front - tp->v_active;
|
|
de_v_end = tp->v_total - tp->v_front - 1;
|
|
|
|
// VENC timing gen is disabled
|
|
regmap_write(regmap, ENCL_VIDEO_EN, 0);
|
|
|
|
// bit[15] shadow en
|
|
regmap_write(regmap, ENCL_VIDEO_MODE, 0x8000);
|
|
// Sampling rate: 1
|
|
regmap_write(regmap, ENCL_VIDEO_MODE_ADV, 0x0418);
|
|
// bypass filter
|
|
regmap_write(regmap, ENCL_VIDEO_FILT_CTRL, 0x1000);
|
|
|
|
//set vtotal & htotal
|
|
regmap_write(regmap, ENCL_VIDEO_MAX_PXCNT, tp->h_total - 1);
|
|
regmap_write(regmap, ENCL_VIDEO_MAX_LNCNT, tp->v_total - 1);
|
|
regmap_write(regmap, ENCL_VIDEO_HAVON_BEGIN, de_h_begin);
|
|
regmap_write(regmap, ENCL_VIDEO_HAVON_END, de_h_end);
|
|
regmap_write(regmap, ENCL_VIDEO_VAVON_BLINE, de_v_begin);
|
|
regmap_write(regmap, ENCL_VIDEO_VAVON_ELINE, de_v_end);
|
|
|
|
// set hsync
|
|
regmap_write(regmap, ENCL_VIDEO_HSO_BEGIN, 0x0);
|
|
regmap_write(regmap, ENCL_VIDEO_HSO_END, tp->h_sync);
|
|
|
|
// set vsync
|
|
regmap_write(regmap, ENCL_VIDEO_VSO_BEGIN, 0x0);
|
|
regmap_write(regmap, ENCL_VIDEO_VSO_END, 0x0);
|
|
regmap_write(regmap, ENCL_VIDEO_VSO_BLINE, 0x0);
|
|
regmap_write(regmap, ENCL_VIDEO_VSO_ELINE, tp->v_sync - 1);
|
|
|
|
// set inbuf
|
|
regmap_write(regmap, ENCL_INBUF_CNTL1, (5 << 13) | (tp->h_sync - 1));
|
|
regmap_write(regmap, ENCL_INBUF_CNTL0, 0x200);
|
|
|
|
regmap_write(regmap, LCD_RGB_BASE_ADDR, 0x0);
|
|
regmap_write(regmap, LCD_RGB_COEFF_ADDR, 0x400);
|
|
regmap_write(regmap, LCD_DITH_CNTL_ADDR, 0x0);
|
|
//regmap_write(regmap, LCD_POL_CNTL_ADDR);
|
|
|
|
// DE signal
|
|
regmap_write(regmap, DE_HS_ADDR, de_h_begin);
|
|
regmap_write(regmap, DE_HE_ADDR, de_h_end);
|
|
regmap_write(regmap, DE_VS_ADDR, de_v_begin);
|
|
regmap_write(regmap, DE_VE_ADDR, de_v_end);
|
|
|
|
// Hsync signal
|
|
regmap_write(regmap, HSYNC_HS_ADDR, 0x0);
|
|
regmap_write(regmap, HSYNC_HE_ADDR, tp->h_sync);
|
|
regmap_write(regmap, HSYNC_VS_ADDR, 0x0);
|
|
regmap_write(regmap, HSYNC_VE_ADDR, tp->v_total - 1);
|
|
|
|
// Vsync signal
|
|
regmap_write(regmap, VSYNC_HS_ADDR, 0x0);
|
|
regmap_write(regmap, VSYNC_HE_ADDR, 0x0);
|
|
regmap_write(regmap, VSYNC_VS_ADDR, 0x0);
|
|
regmap_write(regmap, VSYNC_VE_ADDR, tp->v_sync - 1);
|
|
|
|
regmap_write(regmap, ENCL_VIDEO_RGBIN_CTRL, 3);
|
|
_meson_venc_bist_mode_set(regmap, VENC_ENCL, bist_type);
|
|
regmap_write(regmap, ENCL_VIDEO_EN, 1);
|
|
regmap_write(regmap, VPU_VENC_CTRL, 2);
|
|
}
|
|
|
|
int meson_venc_mode_set(struct meson_tx_venc *venc, u32 enc_index, u32 enc_type,
|
|
enum venc_bist_type bist_type, void *para)
|
|
{
|
|
struct meson_tx_format_para *fmt_para = (struct meson_tx_format_para *)para;
|
|
struct regmap *regmap = venc->regmap[enc_index];
|
|
|
|
if (enc_type == VENC_ENCP)
|
|
config_tv_encp_calc(regmap, fmt_para, bist_type);
|
|
else if (enc_type == VENC_ENCL)
|
|
config_tv_encl_calc(regmap, fmt_para, bist_type);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int meson_venc_bist_mode_set(struct meson_tx_venc *venc, u32 enc_index, u32 enc_type,
|
|
enum venc_bist_type bist_type, void *para)
|
|
{
|
|
struct regmap *regmap = venc->regmap[enc_index];
|
|
|
|
if (enc_type == VENC_ENCP)
|
|
_meson_venc_bist_mode_set(regmap, VENC_ENCP, bist_type);
|
|
else if (enc_type == VENC_ENCL)
|
|
_meson_venc_bist_mode_set(regmap, VENC_ENCL, bist_type);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int meson_venc_mode_check(struct meson_tx_venc *venc, u32 enc_index, void *para)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int meson_venc_mode_disable(struct meson_tx_venc *venc, u32 enc_index, u32 enc_type)
|
|
{
|
|
struct regmap *regmap = venc->regmap[enc_index];
|
|
u32 venc_en_addr = (enc_type == VENC_ENCP) ? ENCP_VIDEO_EN : ENCL_VIDEO_EN;
|
|
|
|
// VENC timing gen is disabled
|
|
regmap_write(regmap, venc_en_addr, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int meson_tx_venc_probe(struct platform_device *pdev)
|
|
{
|
|
int i, ret, num_venc;
|
|
struct resource *res;
|
|
void __iomem *base, *venc_base;
|
|
struct regmap **regmap;
|
|
u32 *venc_offset;
|
|
struct meson_tx_venc *tx_venc;
|
|
struct device *device = &pdev->dev;
|
|
|
|
tx_venc = devm_kmalloc(device, sizeof(*tx_venc), GFP_KERNEL);
|
|
if (!tx_venc)
|
|
return -ENOMEM;
|
|
|
|
num_venc = of_property_count_u32_elems(device->of_node, "venc_offset");
|
|
if (num_venc < 0) {
|
|
pr_err("%s Invalid or missing venc_offset\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = of_property_read_u32_array(device->of_node, "venc_offset",
|
|
tx_venc->venc_offset, num_venc);
|
|
if (ret) {
|
|
pr_err("%s Invalid or missing venc_offset\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!res)
|
|
return -EINVAL;
|
|
|
|
base = devm_ioremap(device, res->start, resource_size(res));
|
|
if (!base)
|
|
return -ENOMEM;
|
|
|
|
regmap = tx_venc->regmap;
|
|
venc_offset = tx_venc->venc_offset;
|
|
tx_venc->num_regmap = num_venc;
|
|
|
|
for (i = 0; i < tx_venc->num_regmap; i++) {
|
|
venc_base = base + (venc_offset[i] << 2);
|
|
regmap[i] = regmap_init_mmio(res->start, venc_base, resource_size(res));
|
|
if (IS_ERR(regmap[i])) {
|
|
pr_err("%s regmap init mmio fail\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
dev_set_drvdata(device, tx_venc);
|
|
|
|
pr_info("%s %px\n", __func__, tx_venc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void meson_tx_venc_remove(struct platform_device *pdev)
|
|
{
|
|
struct device *device = &pdev->dev;
|
|
|
|
pr_info("%s %s\n", __func__, dev_name(device));
|
|
}
|
|
|
|
static const struct of_device_id meson_tx_venc_of_match[] = {
|
|
{
|
|
.compatible = "amlogic, tx-venc-t7c",
|
|
},
|
|
{
|
|
.compatible = "amlogic, tx-venc-a9",
|
|
},
|
|
{},
|
|
};
|
|
|
|
static struct platform_driver meson_tx_venc_driver = {
|
|
.probe = meson_tx_venc_probe,
|
|
.remove = meson_tx_venc_remove,
|
|
.driver = {
|
|
.name = DEVICE_NAME,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_match_ptr(meson_tx_venc_of_match),
|
|
}
|
|
};
|
|
|
|
int __init meson_tx_venc_init(void)
|
|
{
|
|
return platform_driver_register(&meson_tx_venc_driver);
|
|
}
|
|
|
|
void __exit meson_tx_venc_exit(void)
|
|
{
|
|
pr_info("%s\n", __func__);
|
|
platform_driver_unregister(&meson_tx_venc_driver);
|
|
}
|
|
|