blob: c78a323befa24bb32693fe8faf8ac68447243ce7 [file]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
* Author: Chris-YC Chen <chris-yc.chen@mediatek.com>
*/
#include <linux/component.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <mtk_drm_ddp_comp.h>
#include "mtk-mml-color.h"
#include "mtk-mml-core.h"
#include "mtk-mml-driver.h"
#include "mtk-mml-dle-adaptor.h"
#include "mtk-mml-pq-birsz-fw.h"
#define BIRSZ_EN 0x000
#define BIRSZ_SEETING 0x004
#define BIRSZ_SIZE_IN 0x008
#define BIRSZ_SIZE_OUT 0x00c
#define BIRSZ_VERT_STEP 0x010
#define BIRSZ_HORI_STEP 0x014
#define BIRSZ_HORI_INT_OFST 0x018
#define BIRSZ_HORI_SUB_OFST 0x01c
#define BIRSZ_VERT_INT_OFST 0x020
#define BIRSZ_VERT_SUB_OFST 0x024
#define BIRSZ_CFG 0x028
#define BIRSZ_RESET 0x02c
#define BIRSZ_INTEN 0x030
#define BIRSZ_INTSTA 0x034
#define BIRSZ_STATUS 0x040
#define BIRSZ_INPUT_COUNT 0x044
#define BIRSZ_OUTPUT_COUNT 0x048
#define BIRSZ_CHKSUM 0x04c
#define BIRSZ_DUMMY_REG 0x050
#define BIRSZ_ATPG 0x054
#define BIRSZ_SHADOW_CTRL 0x058
#define BIRSZ_DGB_SEL 0x05c
#define BIRSZ_DBG_RDATA 0x060
#define TILE_SCALER_SUBPIXEL_SHIFT 20
struct birsz_data {
u32 tile_width;
bool add_ddp;
};
static const struct birsz_data mt6985_birsz_data = {
.tile_width = 516,
.add_ddp = true,
};
struct mml_comp_birsz {
struct mtk_ddp_comp ddp_comp;
struct mml_comp comp;
const struct birsz_data *data;
bool ddp_bound;
};
/* meta data for each different frame config */
struct birsz_frame_data {
struct birsz_fw_out fw_out;
};
static inline struct birsz_frame_data *birsz_frm_data(struct mml_comp_config *ccfg)
{
return ccfg->data;
}
static inline struct mml_comp_birsz *comp_to_birsz(struct mml_comp *comp)
{
return container_of(comp, struct mml_comp_birsz, comp);
}
static void birsz_first_6_taps(s32 out_start,
s32 out_end,
s32 coef_step,
s32 precision,
s32 crop_offset,
s32 crop_subpixel,
s32 in_max_end,
s32 *in_start,
s32 *in_end)
{
s64 startTemp;
s64 endTemp;
if (crop_subpixel < 0)
crop_subpixel = -0xfffff;
/* Normalize */
crop_subpixel = ((s64)crop_subpixel * precision) >> TILE_SCALER_SUBPIXEL_SHIFT;
startTemp = (s64)out_start * coef_step + (s64)crop_offset * precision + crop_subpixel;
if (startTemp < (s64)3 * precision)
*in_start = 0;
else {
startTemp = startTemp / precision - 3;
if (!(startTemp & 0x1))
*in_start = (s32)startTemp;
else /* must be even */
*in_start = (s32)startTemp - 1;
}
endTemp = (s64)out_end * coef_step +
(s64)crop_offset * precision + crop_subpixel + (3 + 2) * precision;
if (endTemp > (s64)in_max_end * precision)
*in_end = in_max_end;
else {
/* due to ceiling in forward */
endTemp = endTemp / precision;
if (endTemp & 0x1)
*in_end = (s32)endTemp;
else
*in_end = (s32)endTemp + 1;
}
}
static void birsz_second_6_taps(s32 in_start,
s32 coef_step,
s32 precision,
s32 crop_offset,
s32 crop_subpixel,
s32 back_out_start,
s32 *int_ofst,
s32 *sub_ofst)
{
s64 sub_temp;
s64 offset_cal_start;
if (crop_subpixel < 0)
crop_subpixel = -0xfffff;
/* Normalize */
crop_subpixel = ((s64)crop_subpixel * precision) >> TILE_SCALER_SUBPIXEL_SHIFT;
offset_cal_start = back_out_start;
/* Cal bias & offset by fxied backward_out_pos_start */
sub_temp = (s64)offset_cal_start * coef_step +
(s64)crop_offset * precision + crop_subpixel - (s64)in_start * precision;
*int_ofst = (s32)(sub_temp / precision);
*sub_ofst = (s32)(sub_temp - precision * *int_ofst);
if (*sub_ofst < 0) {
*int_ofst -= 1;
*sub_ofst += precision;
}
}
static void birsz_cal_tile(struct mml_tile_engine *ref_tile,
struct mml_tile_engine *tile,
struct birsz_frame_data *birsz_frm,
struct mml_frame_config *cfg)
{
s32 in_x_left;
s32 in_x_right;
s32 out_x_left;
s32 out_x_right;
s32 in_y_top;
s32 in_y_bottom;
s32 int_ofst;
s32 sub_ofst;
out_x_left = ref_tile->in.xs;
out_x_right = ref_tile->in.xe;
mml_msg("%s out_x_right %d, out_x_left %d", __func__, out_x_right, out_x_left);
birsz_first_6_taps(out_x_left,
out_x_right,
birsz_frm->fw_out.hori_step,
birsz_frm->fw_out.precision,
birsz_frm->fw_out.hori_int_ofst,
birsz_frm->fw_out.hori_sub_ofst,
cfg->info.seg_map.width - 1,
&in_x_left,
&in_x_right);
if (in_x_left < 0)
in_x_left = 0;
if (in_x_right + 1 > cfg->info.seg_map.width)
in_x_right = cfg->info.seg_map.width - 1;
mml_msg("in_x_right %d, in_x_left %d, seg_map.width %d",
in_x_right, in_x_left, cfg->info.seg_map.width);
birsz_second_6_taps(in_x_left,
birsz_frm->fw_out.hori_step,
birsz_frm->fw_out.precision,
birsz_frm->fw_out.hori_int_ofst,
birsz_frm->fw_out.hori_sub_ofst,
out_x_left,
&int_ofst,
&sub_ofst);
tile->luma.x = (int_ofst > 0) ? (u32)int_ofst : 0x1ffff;
tile->luma.x_sub = sub_ofst;
mml_msg("luma.x %#010x, luma.x_sub %#010x",
tile->luma.x, tile->luma.x_sub);
mml_msg("%s in.ye %d, in.ys %d", __func__, ref_tile->in.ye, ref_tile->in.ys);
birsz_first_6_taps(ref_tile->in.ys,
ref_tile->in.ye,
birsz_frm->fw_out.vert_step,
birsz_frm->fw_out.precision,
birsz_frm->fw_out.vert_int_ofst,
birsz_frm->fw_out.vert_sub_ofst,
cfg->info.seg_map.height - 1,
&in_y_top,
&in_y_bottom);
mml_msg("in_y_bottom %d, in_y_top %d", in_y_bottom, in_y_top);
birsz_second_6_taps(in_y_top,
birsz_frm->fw_out.vert_step,
birsz_frm->fw_out.precision,
birsz_frm->fw_out.vert_int_ofst,
birsz_frm->fw_out.vert_sub_ofst,
ref_tile->in.ys,
&int_ofst,
&sub_ofst);
tile->luma.y = (int_ofst > 0) ? (u32)int_ofst : 0x1ffff;
tile->luma.y_sub = sub_ofst;
mml_msg("luma.y %#010x, luma.y_sub %#010x", tile->luma.y, tile->luma.y_sub);
tile->in.xs = in_x_left;
tile->in.xe = in_x_right;
tile->in.ys = in_y_top;
tile->in.ye = in_y_bottom;
tile->out.xs = out_x_left;
tile->out.xe = out_x_right;
tile->out.ys = ref_tile->in.ys;
tile->out.ye = ref_tile->in.ye;
}
static s32 birsz_region_pq_bw(struct mml_comp *comp, struct mml_task *task,
struct mml_comp_config *ccfg,
struct mml_tile_engine *ref_tile,
struct mml_tile_engine *tile)
{
struct birsz_frame_data *birsz_frm = birsz_frm_data(ccfg);
struct mml_frame_config *cfg = task->config;
birsz_cal_tile(ref_tile, tile, birsz_frm, cfg);
return 0;
}
static const struct mml_comp_tile_ops birsz_tile_ops = {
.region_pq_bw = birsz_region_pq_bw,
};
static s32 birsz_prepare(struct mml_comp *comp, struct mml_task *task,
struct mml_comp_config *ccfg)
{
struct birsz_frame_data *birsz_frm = NULL;
struct mml_frame_config *cfg = task->config;
const struct mml_frame_data *src = &cfg->info.seg_map;
const struct mml_frame_dest *dest = &cfg->info.dest[0];
s32 ret = 0;
struct birsz_fw_in fw_in;
struct birsz_fw_out fw_out;
birsz_frm = kzalloc(sizeof(*birsz_frm), GFP_KERNEL);
ccfg->data = birsz_frm;
fw_in.in_width = src->width;
fw_in.in_height = src->height;
if (dest->rotate == MML_ROT_90 || dest->rotate == MML_ROT_270) {
fw_in.out_width = dest->data.height;
fw_in.out_height = dest->data.width;
} else {
fw_in.out_width = dest->data.width;
fw_in.out_height = dest->data.height;
}
birsz_fw(&fw_in, &fw_out);
birsz_frm->fw_out.precision = fw_out.precision;
birsz_frm->fw_out.hori_step = fw_out.hori_step;
birsz_frm->fw_out.hori_int_ofst = fw_out.hori_int_ofst;
birsz_frm->fw_out.hori_sub_ofst =
((s64)fw_out.hori_sub_ofst << TILE_SCALER_SUBPIXEL_SHIFT) /
fw_out.precision;
birsz_frm->fw_out.vert_step = fw_out.vert_step;
birsz_frm->fw_out.vert_int_ofst = fw_out.vert_int_ofst;
birsz_frm->fw_out.vert_sub_ofst =
((s64)fw_out.vert_sub_ofst << TILE_SCALER_SUBPIXEL_SHIFT) /
fw_out.precision;
return ret;
}
static void birsz_init(struct cmdq_pkt *pkt, const phys_addr_t base_pa)
{
cmdq_pkt_write(pkt, NULL, base_pa + BIRSZ_EN, 0x1, U32_MAX);
/* Enable shadow */
cmdq_pkt_write(pkt, NULL, base_pa + BIRSZ_SHADOW_CTRL, 0x2, U32_MAX);
}
static s32 birsz_config_init(struct mml_comp *comp, struct mml_task *task,
struct mml_comp_config *ccfg)
{
birsz_init(task->pkts[ccfg->pipe], comp->base_pa);
return 0;
}
static s32 birsz_config_frame(struct mml_comp *comp, struct mml_task *task,
struct mml_comp_config *ccfg)
{
struct birsz_frame_data *birsz_frm = birsz_frm_data(ccfg);
struct cmdq_pkt *pkt = task->pkts[ccfg->pipe];
const phys_addr_t base_pa = comp->base_pa;
mml_msg("%s is called", __func__);
mml_msg("vert_step %#010x, hori_step %#010x",
birsz_frm->fw_out.vert_step, birsz_frm->fw_out.hori_step);
cmdq_pkt_write(pkt, NULL, base_pa + BIRSZ_VERT_STEP,
birsz_frm->fw_out.vert_step, 0x7ffff);
cmdq_pkt_write(pkt, NULL, base_pa + BIRSZ_HORI_STEP,
birsz_frm->fw_out.hori_step, 0x7ffff);
mml_msg("%s is end", __func__);
return 0;
}
static s32 birsz_config_tile(struct mml_comp *comp, struct mml_task *task,
struct mml_comp_config *ccfg, u32 idx)
{
struct mml_frame_config *cfg = task->config;
struct cmdq_pkt *pkt = task->pkts[ccfg->pipe];
const phys_addr_t base_pa = comp->base_pa;
struct mml_tile_engine *tile = config_get_tile(cfg, ccfg, idx);
u32 birsz_input_w = 0;
u32 birsz_input_h = 0;
u32 birsz_output_w = 0;
u32 birsz_output_h = 0;
mml_msg("%s idx[%d]", __func__, idx);
birsz_input_w = tile->in.xe - tile->in.xs + 1;
birsz_input_h = tile->in.ye - tile->in.ys + 1;
birsz_output_w = tile->out.xe - tile->out.xs + 1;
birsz_output_h = tile->out.ye - tile->out.ys + 1;
mml_msg("%s in.xe %d, in.xs %d, in.ye %d, in.ys %d",
__func__, tile->in.xe, tile->in.xs, tile->in.ye, tile->in.ys);
mml_msg("%s out.xe %d, out.xs %d, out.ye %d, out.ys %d",
__func__, tile->out.xe, tile->out.xs, tile->out.ye, tile->out.ys);
mml_msg("%s luma.x %#010x, luma.x_sub %#010x, luma.y %#010x, luma.y_sub %#010x",
__func__, tile->luma.x, tile->luma.x_sub, tile->luma.y, tile->luma.y_sub);
cmdq_pkt_write(pkt, NULL, base_pa + BIRSZ_SIZE_IN,
(birsz_input_h << 16) + birsz_input_w, U32_MAX);
cmdq_pkt_write(pkt, NULL, base_pa + BIRSZ_SIZE_OUT,
(birsz_output_h << 16) + birsz_output_w, U32_MAX);
cmdq_pkt_write(pkt, NULL, base_pa + BIRSZ_HORI_INT_OFST,
tile->luma.x, 0x1ffff);
cmdq_pkt_write(pkt, NULL, base_pa + BIRSZ_HORI_SUB_OFST,
tile->luma.x_sub, 0x7fff);
cmdq_pkt_write(pkt, NULL, base_pa + BIRSZ_VERT_INT_OFST,
tile->luma.y, 0x1ffff);
cmdq_pkt_write(pkt, NULL, base_pa + BIRSZ_VERT_SUB_OFST,
tile->luma.y_sub, 0x7fff);
return 0;
}
static const struct mml_comp_config_ops birsz_cfg_ops = {
.prepare = birsz_prepare,
.init = birsz_config_init,
.frame = birsz_config_frame,
.tile = birsz_config_tile,
};
static const struct mml_comp_hw_ops birsz_hw_ops = {
.clk_enable = &mml_comp_clk_enable,
.clk_disable = &mml_comp_clk_disable,
};
static void birsz_debug_dump(struct mml_comp *comp)
{
void __iomem *base = comp->base;
u32 value[20];
u32 debug[8];
u32 shadow_ctrl;
mml_err("birsz component %u dump:", comp->id);
/* Enable shadow read working */
shadow_ctrl = readl(base + BIRSZ_SHADOW_CTRL);
shadow_ctrl |= 0x4;
writel(shadow_ctrl, base + BIRSZ_SHADOW_CTRL);
value[0] = readl(base + BIRSZ_EN);
value[1] = readl(base + BIRSZ_SEETING);
value[2] = readl(base + BIRSZ_SIZE_IN);
value[3] = readl(base + BIRSZ_SIZE_OUT);
value[4] = readl(base + BIRSZ_VERT_STEP);
value[5] = readl(base + BIRSZ_HORI_STEP);
value[6] = readl(base + BIRSZ_HORI_INT_OFST);
value[7] = readl(base + BIRSZ_HORI_SUB_OFST);
value[8] = readl(base + BIRSZ_VERT_INT_OFST);
value[9] = readl(base + BIRSZ_VERT_SUB_OFST);
value[10] = readl(base + BIRSZ_CFG);
value[11] = readl(base + BIRSZ_RESET);
value[12] = readl(base + BIRSZ_INTEN);
value[13] = readl(base + BIRSZ_INTSTA);
value[14] = readl(base + BIRSZ_STATUS);
value[15] = readl(base + BIRSZ_INPUT_COUNT);
value[16] = readl(base + BIRSZ_OUTPUT_COUNT);
value[17] = readl(base + BIRSZ_CHKSUM);
value[18] = readl(base + BIRSZ_DUMMY_REG);
value[19] = readl(base + BIRSZ_ATPG);
writel(0x1, base + BIRSZ_DGB_SEL);
debug[0] = readl(base + BIRSZ_DBG_RDATA);
writel(0x2, base + BIRSZ_DGB_SEL);
debug[1] = readl(base + BIRSZ_DBG_RDATA);
writel(0x3, base + BIRSZ_DGB_SEL);
debug[2] = readl(base + BIRSZ_DBG_RDATA);
writel(0x9, base + BIRSZ_DGB_SEL);
debug[3] = readl(base + BIRSZ_DBG_RDATA);
writel(0xa, base + BIRSZ_DGB_SEL);
debug[4] = readl(base + BIRSZ_DBG_RDATA);
writel(0xb, base + BIRSZ_DGB_SEL);
debug[5] = readl(base + BIRSZ_DBG_RDATA);
writel(0xd, base + BIRSZ_DGB_SEL);
debug[6] = readl(base + BIRSZ_DBG_RDATA);
writel(0xe, base + BIRSZ_DGB_SEL);
debug[7] = readl(base + BIRSZ_DBG_RDATA);
mml_err("BIRSZ_EN %#010x BIRSZ_SEETING %#010x",
value[0], value[1]);
mml_err("BIRSZ_SIZE_IN %#010x BIRSZ_SIZE_OUT %#010x",
value[2], value[3]);
mml_err("BIRSZ_VERT_STEP %#010x BIRSZ_HORI_STEP %#010x",
value[4], value[5]);
mml_err("BIRSZ_HORI_INT_OFST %#010x BIRSZ_HORI_SUB_OFST %#010x",
value[6], value[7]);
mml_err("BIRSZ_VERT_INT_OFST %#010x BIRSZ_VERT_SUB_OFST %#010x",
value[8], value[9]);
mml_err("BIRSZ_CFG %#010x BIRSZ_RESET %#010x",
value[10], value[11]);
mml_err("BIRSZ_INTEN %#010x BIRSZ_INTSTA %#010x BIRSZ_STATUS %#010x",
value[12], value[13], value[14]);
mml_err("BIRSZ_INPUT_COUNT %#010x BIRSZ_OUTPUT_COUNT %#010x",
value[15], value[16]);
mml_err("BIRSZ_CHKSUM %#010x BIRSZ_DUMMY_REG %#010x BIRSZ_ATPG %#010x",
value[17], value[18], value[19]);
mml_err("BIRSZ_DEBUG_1 %#010x BIRSZ_DEBUG_2 %#010x BIRSZ_DEBUG_3 %#010x",
debug[0], debug[1], debug[2]);
mml_err("BIRSZ_DEBUG_9 %#010x BIRSZ_DEBUG_10 %#010x BIRSZ_DEBUG_11 %#010x",
debug[3], debug[4], debug[5]);
mml_err("BIRSZ_DEBUG_13 %#010x BIRSZ_DEBUG_14 %#010x",
debug[6], debug[7]);
}
static const struct mml_comp_debug_ops birsz_debug_ops = {
.dump = &birsz_debug_dump,
};
static int mml_bind(struct device *dev, struct device *master, void *data)
{
struct mml_comp_birsz *birsz = dev_get_drvdata(dev);
struct drm_device *drm_dev = data;
s32 ret;
if (!drm_dev) {
ret = mml_register_comp(master, &birsz->comp);
if (ret)
dev_err(dev, "Failed to register mml component %s: %d\n",
dev->of_node->full_name, ret);
} else {
ret = mml_ddp_comp_register(drm_dev, &birsz->ddp_comp);
if (ret)
dev_err(dev, "Failed to register ddp component %s: %d\n",
dev->of_node->full_name, ret);
else
birsz->ddp_bound = true;
}
return ret;
}
static void mml_unbind(struct device *dev, struct device *master, void *data)
{
struct mml_comp_birsz *birsz = dev_get_drvdata(dev);
struct drm_device *drm_dev = data;
if (!drm_dev) {
mml_unregister_comp(master, &birsz->comp);
} else {
mml_ddp_comp_unregister(drm_dev, &birsz->ddp_comp);
birsz->ddp_bound = false;
}
}
static const struct component_ops mml_comp_ops = {
.bind = mml_bind,
.unbind = mml_unbind,
};
static const struct mtk_ddp_comp_funcs ddp_comp_funcs = {
};
static struct mml_comp_birsz *dbg_probed_components[4];
static int dbg_probed_count;
static int probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mml_comp_birsz *priv;
s32 ret;
bool add_ddp = true;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
platform_set_drvdata(pdev, priv);
priv->data = of_device_get_match_data(dev);
ret = mml_comp_init(pdev, &priv->comp);
if (ret) {
dev_err(dev, "Failed to init mml component: %d\n", ret);
return ret;
}
/* assign ops */
priv->comp.tile_ops = &birsz_tile_ops;
priv->comp.config_ops = &birsz_cfg_ops;
priv->comp.hw_ops = &birsz_hw_ops;
priv->comp.debug_ops = &birsz_debug_ops;
if (priv->data->add_ddp) {
ret = mml_ddp_comp_init(dev, &priv->ddp_comp, &priv->comp,
&ddp_comp_funcs);
if (ret) {
mml_log("failed to init ddp component: %d", ret);
add_ddp = false;
}
}
dbg_probed_components[dbg_probed_count++] = priv;
ret = component_add(dev, &mml_comp_ops);
if (add_ddp)
ret = component_add(dev, &mml_comp_ops);
if (ret)
dev_err(dev, "Failed to add component: %d\n", ret);
return ret;
}
static int remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &mml_comp_ops);
component_del(&pdev->dev, &mml_comp_ops);
return 0;
}
const struct of_device_id mml_pq_birsz_driver_dt_match[] = {
{
.compatible = "mediatek,mt6985-mml_birsz",
.data = &mt6985_birsz_data,
},
{},
};
MODULE_DEVICE_TABLE(of, mml_pq_birsz_driver_dt_match);
struct platform_driver mml_pq_birsz_driver = {
.probe = probe,
.remove = remove,
.driver = {
.name = "mediatek-mml-pq-birsz",
.owner = THIS_MODULE,
.of_match_table = mml_pq_birsz_driver_dt_match,
},
};
//module_platform_driver(mml_pq_birsz_driver);
static s32 dbg_case;
static s32 dbg_set(const char *val, const struct kernel_param *kp)
{
s32 result;
result = kstrtos32(val, 0, &dbg_case);
mml_log("%s: debug_case=%d", __func__, dbg_case);
switch (dbg_case) {
case 0:
mml_log("use read to dump component status");
break;
default:
mml_err("invalid debug_case: %d", dbg_case);
break;
}
return result;
}
static s32 dbg_get(char *buf, const struct kernel_param *kp)
{
s32 length = 0;
u32 i;
switch (dbg_case) {
case 0:
length += snprintf(buf + length, PAGE_SIZE - length,
"[%d] probed count: %d\n", dbg_case, dbg_probed_count);
for (i = 0; i < dbg_probed_count; i++) {
struct mml_comp *comp = &dbg_probed_components[i]->comp;
length += snprintf(buf + length, PAGE_SIZE - length,
" - [%d] mml comp_id: %d.%d @%08x name: %s bound: %d\n", i,
comp->id, comp->sub_idx, comp->base_pa,
comp->name ? comp->name : "(null)", comp->bound);
length += snprintf(buf + length, PAGE_SIZE - length,
" - larb_port: %d @%llx pw: %d clk: %d\n",
comp->larb_port, comp->larb_base,
comp->pw_cnt, comp->clk_cnt);
length += snprintf(buf + length, PAGE_SIZE - length,
" - ddp comp_id: %d bound: %d\n",
dbg_probed_components[i]->ddp_comp.id,
dbg_probed_components[i]->ddp_bound);
}
break;
default:
mml_err("not support read for debug_case: %d", dbg_case);
break;
}
buf[length] = '\0';
return length;
}
static const struct kernel_param_ops dbg_param_ops = {
.set = dbg_set,
.get = dbg_get,
};
module_param_cb(pq_birsz_debug, &dbg_param_ops, NULL, 0644);
MODULE_PARM_DESC(pq_birsz_debug, "mml birsz debug case");
MODULE_AUTHOR("Dennis-YC Hsieh <dennis-yc.hsieh@mediatek.com>");
MODULE_DESCRIPTION("MediaTek SoC display MML BIRSZ driver");
MODULE_LICENSE("GPL v2");