blob: 1c9fd1ff70b67e0b17e766be44b80b7d8492401a [file] [log] [blame]
/*
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Helper file for Samsung EXYNOS DPU driver
*
* 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/err.h>
#include <linux/pm_runtime.h>
#include <asm/cacheflush.h>
#include <asm/page.h>
#if defined(CONFIG_EXYNOS_CONTENT_PATH_PROTECTION)
#include <linux/smc.h>
#endif
#include <linux/exynos_iovmm.h>
#include "decon.h"
#include "dsim.h"
#include "dpp.h"
#include "displayport.h"
#include "./panels/lcd_ctrl.h"
#include <video/mipi_display.h>
static int __dpu_match_dev(struct device *dev, void *data)
{
struct dpp_device *dpp;
struct dsim_device *dsim;
struct displayport_device *displayport;
struct decon_device *decon = (struct decon_device *)data;
decon_dbg("%s: drvname(%s)\n", __func__, dev->driver->name);
if (!strcmp(DPP_MODULE_NAME, dev->driver->name)) {
dpp = (struct dpp_device *)dev_get_drvdata(dev);
decon->dpp_sd[dpp->id] = &dpp->sd;
decon_dbg("dpp%d sd name(%s)\n", dpp->id,
decon->dpp_sd[dpp->id]->name);
} else if (!strcmp(DSIM_MODULE_NAME, dev->driver->name)) {
dsim = (struct dsim_device *)dev_get_drvdata(dev);
decon->dsim_sd[dsim->id] = &dsim->sd;
decon_dbg("dsim sd name(%s)\n", dsim->sd.name);
} else if (!strcmp(DISPLAYPORT_MODULE_NAME, dev->driver->name)) {
displayport = (struct displayport_device *)dev_get_drvdata(dev);
decon->displayport_sd = &displayport->sd;
decon_dbg("displayport sd name(%s)\n", displayport->sd.name);
} else {
decon_err("failed to get driver name\n");
}
return 0;
}
int dpu_get_sd_by_drvname(struct decon_device *decon, char *drvname)
{
struct device_driver *drv;
struct device *dev;
drv = driver_find(drvname, &platform_bus_type);
if (IS_ERR_OR_NULL(drv)) {
decon_err("failed to find driver\n");
return -ENODEV;
}
dev = driver_find_device(drv, NULL, decon, __dpu_match_dev);
return 0;
}
u32 dpu_translate_fmt_to_dpp(u32 format)
{
switch (format) {
case DECON_PIXEL_FORMAT_NV12:
return DECON_PIXEL_FORMAT_NV21;
case DECON_PIXEL_FORMAT_NV21:
return DECON_PIXEL_FORMAT_NV12;
case DECON_PIXEL_FORMAT_NV12M:
return DECON_PIXEL_FORMAT_NV21M;
case DECON_PIXEL_FORMAT_NV21M:
return DECON_PIXEL_FORMAT_NV12M;
case DECON_PIXEL_FORMAT_NV12N:
return DECON_PIXEL_FORMAT_NV12N;
case DECON_PIXEL_FORMAT_YUV420:
return DECON_PIXEL_FORMAT_YVU420;
case DECON_PIXEL_FORMAT_YVU420:
return DECON_PIXEL_FORMAT_YUV420;
case DECON_PIXEL_FORMAT_YUV420M:
return DECON_PIXEL_FORMAT_YVU420M;
case DECON_PIXEL_FORMAT_YVU420M:
return DECON_PIXEL_FORMAT_YUV420M;
case DECON_PIXEL_FORMAT_ARGB_8888:
return DECON_PIXEL_FORMAT_BGRA_8888;
case DECON_PIXEL_FORMAT_ABGR_8888:
return DECON_PIXEL_FORMAT_RGBA_8888;
case DECON_PIXEL_FORMAT_RGBA_8888:
return DECON_PIXEL_FORMAT_ABGR_8888;
case DECON_PIXEL_FORMAT_BGRA_8888:
return DECON_PIXEL_FORMAT_ARGB_8888;
case DECON_PIXEL_FORMAT_XRGB_8888:
return DECON_PIXEL_FORMAT_BGRX_8888;
case DECON_PIXEL_FORMAT_XBGR_8888:
return DECON_PIXEL_FORMAT_RGBX_8888;
case DECON_PIXEL_FORMAT_RGBX_8888:
return DECON_PIXEL_FORMAT_XBGR_8888;
case DECON_PIXEL_FORMAT_BGRX_8888:
return DECON_PIXEL_FORMAT_XRGB_8888;
default:
return format;
}
}
u32 dpu_get_bpp(enum decon_pixel_format fmt)
{
switch (fmt) {
case DECON_PIXEL_FORMAT_ARGB_8888:
case DECON_PIXEL_FORMAT_ABGR_8888:
case DECON_PIXEL_FORMAT_RGBA_8888:
case DECON_PIXEL_FORMAT_BGRA_8888:
case DECON_PIXEL_FORMAT_XRGB_8888:
case DECON_PIXEL_FORMAT_XBGR_8888:
case DECON_PIXEL_FORMAT_RGBX_8888:
case DECON_PIXEL_FORMAT_BGRX_8888:
return 32;
case DECON_PIXEL_FORMAT_RGBA_5551:
case DECON_PIXEL_FORMAT_RGB_565:
case DECON_PIXEL_FORMAT_NV16:
case DECON_PIXEL_FORMAT_NV61:
case DECON_PIXEL_FORMAT_YVU422_3P:
return 16;
case DECON_PIXEL_FORMAT_NV12:
case DECON_PIXEL_FORMAT_NV21:
case DECON_PIXEL_FORMAT_NV12M:
case DECON_PIXEL_FORMAT_NV21M:
case DECON_PIXEL_FORMAT_YUV420:
case DECON_PIXEL_FORMAT_YVU420:
case DECON_PIXEL_FORMAT_YUV420M:
case DECON_PIXEL_FORMAT_YVU420M:
return 12;
default:
break;
}
return 0;
}
int dpu_get_plane_cnt(enum decon_pixel_format format)
{
switch (format) {
case DECON_PIXEL_FORMAT_ARGB_8888:
case DECON_PIXEL_FORMAT_ABGR_8888:
case DECON_PIXEL_FORMAT_RGBA_8888:
case DECON_PIXEL_FORMAT_BGRA_8888:
case DECON_PIXEL_FORMAT_XRGB_8888:
case DECON_PIXEL_FORMAT_XBGR_8888:
case DECON_PIXEL_FORMAT_RGBX_8888:
case DECON_PIXEL_FORMAT_BGRX_8888:
case DECON_PIXEL_FORMAT_RGBA_5551:
case DECON_PIXEL_FORMAT_RGB_565:
case DECON_PIXEL_FORMAT_NV12N:
return 1;
case DECON_PIXEL_FORMAT_NV16:
case DECON_PIXEL_FORMAT_NV61:
case DECON_PIXEL_FORMAT_NV12:
case DECON_PIXEL_FORMAT_NV21:
case DECON_PIXEL_FORMAT_NV12M:
case DECON_PIXEL_FORMAT_NV21M:
return 2;
case DECON_PIXEL_FORMAT_YVU422_3P:
case DECON_PIXEL_FORMAT_YUV420:
case DECON_PIXEL_FORMAT_YVU420:
case DECON_PIXEL_FORMAT_YUV420M:
case DECON_PIXEL_FORMAT_YVU420M:
return 3;
default:
decon_err("invalid format(%d)\n", format);
return 1;
}
}
u32 dpu_get_alpha_len(int format)
{
switch (format) {
case DECON_PIXEL_FORMAT_RGBA_8888:
case DECON_PIXEL_FORMAT_BGRA_8888:
return 8;
case DECON_PIXEL_FORMAT_RGBA_5551:
return 1;
case DECON_PIXEL_FORMAT_RGBX_8888:
case DECON_PIXEL_FORMAT_RGB_565:
case DECON_PIXEL_FORMAT_BGRX_8888:
return 0;
default:
return 0;
}
}
bool decon_intersect(struct decon_rect *r1, struct decon_rect *r2)
{
return !(r1->left > r2->right || r1->right < r2->left ||
r1->top > r2->bottom || r1->bottom < r2->top);
}
int decon_intersection(struct decon_rect *r1,
struct decon_rect *r2, struct decon_rect *r3)
{
r3->top = max(r1->top, r2->top);
r3->bottom = min(r1->bottom, r2->bottom);
r3->left = max(r1->left, r2->left);
r3->right = min(r1->right, r2->right);
return 0;
}
bool is_decon_rect_differ(struct decon_rect *r1, struct decon_rect *r2)
{
return ((r1->left != r2->left) || (r1->top != r2->top) ||
(r1->right != r2->right) || (r1->bottom != r2->bottom));
}
bool is_scaling(struct decon_win_config *config)
{
return (config->dst.w != config->src.w) || (config->dst.h != config->src.h);
}
bool is_full(struct decon_rect *r, struct decon_lcd *lcd)
{
return (r->left == 0) && (r->top == 0) &&
(r->right == lcd->xres - 1) && (r->bottom == lcd->yres - 1);
}
bool is_rgb32(int format)
{
switch (format) {
case DECON_PIXEL_FORMAT_ARGB_8888:
case DECON_PIXEL_FORMAT_ABGR_8888:
case DECON_PIXEL_FORMAT_RGBA_8888:
case DECON_PIXEL_FORMAT_BGRA_8888:
case DECON_PIXEL_FORMAT_XRGB_8888:
case DECON_PIXEL_FORMAT_XBGR_8888:
case DECON_PIXEL_FORMAT_RGBX_8888:
case DECON_PIXEL_FORMAT_BGRX_8888:
return true;
default:
return false;
}
}
bool is_decon_opaque_format(int format)
{
switch (format) {
case DECON_PIXEL_FORMAT_RGBA_8888:
case DECON_PIXEL_FORMAT_BGRA_8888:
case DECON_PIXEL_FORMAT_RGBA_5551:
case DECON_PIXEL_FORMAT_ARGB_8888:
case DECON_PIXEL_FORMAT_ABGR_8888:
return false;
default:
return true;
}
}
void dpu_unify_rect(struct decon_rect *r1, struct decon_rect *r2,
struct decon_rect *dst)
{
dst->top = min(r1->top, r2->top);
dst->bottom = max(r1->bottom, r2->bottom);
dst->left = min(r1->right, r2->right);
dst->right = max(r1->right, r2->right);
}
void decon_to_psr_info(struct decon_device *decon, struct decon_mode_info *psr)
{
psr->psr_mode = decon->dt.psr_mode;
psr->trig_mode = decon->dt.trig_mode;
psr->dsi_mode = decon->dt.dsi_mode;
psr->out_type = decon->dt.out_type;
}
void decon_to_init_param(struct decon_device *decon, struct decon_param *p)
{
struct decon_lcd *lcd_info = decon->lcd_info;
struct v4l2_mbus_framefmt mbus_fmt;
mbus_fmt.width = 0;
mbus_fmt.height = 0;
mbus_fmt.code = 0;
mbus_fmt.field = 0;
mbus_fmt.colorspace = 0;
p->lcd_info = lcd_info;
p->psr.psr_mode = decon->dt.psr_mode;
p->psr.trig_mode = decon->dt.trig_mode;
p->psr.dsi_mode = decon->dt.dsi_mode;
p->psr.out_type = decon->dt.out_type;
p->nr_windows = decon->dt.max_win;
p->disp_ss_regs = decon->res.ss_regs;
decon_dbg("%s: psr(%d) trig(%d) dsi(%d) out(%d) wins(%d) LCD[%d %d]\n",
__func__, p->psr.psr_mode, p->psr.trig_mode,
p->psr.dsi_mode, p->psr.out_type, p->nr_windows,
decon->lcd_info->xres, decon->lcd_info->yres);
}
/* sync fence related functions */
void decon_create_timeline(struct decon_device *decon, char *name)
{
decon->timeline = sw_sync_timeline_create(name);
if (decon->dt.out_type == DECON_OUT_DSI)
decon->timeline_max = 1;
else if (decon->dt.out_type == DECON_OUT_WB)
decon->timeline_max = 0;
else
decon->timeline_max = 1;
}
int decon_create_fence(struct decon_device *decon)
{
struct sync_fence *fence;
struct sync_pt *pt;
int fd = -EMFILE;
decon->timeline_max++;
pt = sw_sync_pt_create(decon->timeline, decon->timeline_max);
if (!pt) {
decon_err("%s: failed to create sync pt\n", __func__);
goto err;
}
fence = sync_fence_create("display", pt);
if (!fence) {
decon_err("%s: failed to create fence\n", __func__);
sync_pt_free(pt);
goto err;
}
fd = get_unused_fd_flags(0);
if (fd < 0) {
decon_err("%s: failed to get unused fd\n", __func__);
goto err;
}
sync_fence_install(fence, fd);
return fd;
err:
decon->timeline_max--;
return fd;
}
void decon_wait_fence(struct sync_fence *fence)
{
int err = sync_fence_wait(fence, 900);
if (err >= 0)
return;
if (err < 0)
decon_warn("error waiting on acquire fence: %d\n", err);
}
void decon_signal_fence(struct decon_device *decon)
{
sw_sync_timeline_inc(decon->timeline, 1);
}
void dpu_debug_printk(const char *function_name, const char *format, ...)
{
struct va_format vaf;
va_list args;
va_start(args, format);
vaf.fmt = format;
vaf.va = &args;
printk(KERN_INFO "[%s] %pV", function_name, &vaf);
va_end(args);
}
void __iomem *dpu_get_sysreg_addr(void)
{
void __iomem *regs;
if (of_have_populated_dt()) {
struct device_node *nd;
nd = of_find_compatible_node(NULL, NULL,
"samsung,exynos8-disp_ss");
if (!nd) {
decon_err("failed find compatible node(sysreg-disp)");
return NULL;
}
regs = of_iomap(nd, 0);
if (!regs) {
decon_err("Failed to get sysreg-disp address.");
return NULL;
}
} else {
decon_err("failed have populated device tree");
return NULL;
}
decon_dbg("%s: default sysreg value(0x%x)\n", __func__, readl(regs));
return regs;
}
void __iomem *dpu_get_version_addr(void)
{
void __iomem *regs;
if (of_have_populated_dt()) {
struct device_node *nd;
nd = of_find_compatible_node(NULL, NULL,
"samsung,exynos8-disp-ver");
if (!nd) {
decon_err("failed find compatible node(disp-ver)");
return NULL;
}
regs = of_iomap(nd, 0);
if (!regs) {
decon_err("Failed to get disp-ver address.");
return NULL;
}
} else {
decon_err("failed have populated device tree");
return NULL;
}
decon_dbg("%s: default ver value(0x%x)\n", __func__, readl(regs));
return regs;
}
/*
* DMA_CH0 : VGF0/VGF1
* DMA_CH1 : G0-VG0
* DMA_CH2 : G1-VG1
*/
u32 dpu_dma_type_to_channel(enum decon_idma_type type)
{
u32 ch_id;
switch (type) {
case IDMA_G0_S:
case IDMA_G0:
ch_id = 5;
break;
case IDMA_G1:
ch_id = 3;
break;
case IDMA_VG0:
ch_id = 0;
break;
case IDMA_VG1:
ch_id = 4;
break;
case IDMA_VGF0:
ch_id = 1;
break;
case IDMA_VGF1:
ch_id = 2;
break;
default:
decon_dbg("channel(0x%x) is not valid\n", type);
return -EINVAL;
}
return ch_id;
}
#if defined(CONFIG_EXYNOS_CONTENT_PATH_PROTECTION)
static int decon_get_protect_id(int dma_id)
{
int prot_id = 0;
switch (dma_id) {
case IDMA_G0:
prot_id = PROT_G0;
break;
case IDMA_G1:
prot_id = PROT_G1;
break;
case IDMA_VG0:
prot_id = PROT_VG0;
break;
case IDMA_VG1:
prot_id = PROT_VG1;
break;
case IDMA_VGF0:
prot_id = PROT_VGR0;
break;
case IDMA_VGF1:
prot_id = PROT_VGR1;
break;
case ODMA_WB:
prot_id = PROT_WB1;
break;
default:
decon_err("Unknown DMA_ID (%d)\n", dma_id);
break;
}
return prot_id;
}
static int decon_control_protection(int dma_id, bool en)
{
int ret = SUCCESS_EXYNOS_SMC;
int prot_id;
prot_id = decon_get_protect_id(dma_id);
ret = exynos_smc(SMC_PROTECTION_SET, 0, prot_id,
(en ? SMC_PROTECTION_ENABLE : SMC_PROTECTION_DISABLE));
if (ret)
decon_err("DMA%d (en=%d): exynos_smc call fail (err=%d)\n",
dma_id, en, ret);
else
decon_dbg("DMA%d protection %s\n",
dma_id, en ? "enabled" : "disabled");
return ret;
}
static bool decon_get_protected_idma(struct decon_device *decon, u32 prot_bits)
{
int i;
u32 used_idma = 0;
for (i = 0; i < MAX_DPP_CNT; i++)
if ((prot_bits & (1 << i)) >> i)
used_idma++;
if (used_idma)
return true;
else
return false;
}
void decon_set_protected_content(struct decon_device *decon,
struct decon_reg_data *regs)
{
bool en;
int dma_id, i, ret = 0;
u32 change = 0;
u32 cur_protect_bits = 0;
/* IDMA protection configs (G0,G1,VG0,VG1,VGF0,VGF1) */
for (i = 0; i < decon->dt.max_win; i++) {
if (!regs)
break;
cur_protect_bits |=
(regs->protection[i] << regs->dpp_config[i].idma_type);
}
/* ODMA protection config (WB: writeback) */
if (decon->dt.out_type == DECON_OUT_WB)
if (decon_get_protected_idma(decon, cur_protect_bits))
cur_protect_bits |= (1 << ODMA_WB);
if (decon->prev_protection_bitmask != cur_protect_bits) {
/* apply protection configs for each DMA */
for (dma_id = 0; dma_id <= MAX_DPP_CNT; dma_id++) {
en = cur_protect_bits & (1 << dma_id);
change = (cur_protect_bits & (1 << dma_id)) ^
(decon->prev_protection_bitmask & (1 << dma_id));
if (change) {
if (decon->version == 0) {
/**
* if the shadowed-TZPC is supported,
* DPP_WAIT_IDLE is not necessary
*/
struct v4l2_subdev *sd = NULL;
unsigned long wait_to = 20*1000; /* 20ms */
sd = decon->dpp_sd[dma_id];
v4l2_subdev_call(sd, core, ioctl,
DPP_WAIT_IDLE, (void *)wait_to);
}
ret = decon_control_protection(dma_id, en);
}
}
}
/* save current portection configs */
decon->prev_protection_bitmask = cur_protect_bits;
}
#endif
/* id : VGF0=0, VGF1=1 */
void dpu_dump_data_to_console(void *v_addr, int buf_size, int id)
{
dpp_info("=== (IDMA_VGF%d) Frame Buffer Data(128 Bytes) ===\n",
id == IDMA_VGF0 ? 0 : 1);
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4,
v_addr, buf_size, false);
}