| /* SPDX-License-Identifier: GPL-2.0 */ |
| /* |
| * Copyright (c) 2021 MediaTek Inc. |
| */ |
| |
| #include <linux/component.h> |
| #include <linux/module.h> |
| #include <linux/of_device.h> |
| #include <linux/platform_device.h> |
| |
| #include "mtk-mml-core.h" |
| #include "mtk-mml-driver.h" |
| |
| #include "tile_driver.h" |
| |
| #define FG_TRIGGER 0x000 |
| #define FG_STATUS 0x004 |
| #define FG_CTRL_0 0x020 |
| #define FG_CK_EN 0x024 |
| #define FG_BACK_DOOR_0 0x02c |
| #define FG_PIC_INFO_0 0x400 |
| #define FG_PIC_INFO_1 0x404 |
| #define FG_TILE_INFO_0 0x418 |
| #define FG_TILE_INFO_1 0x41c |
| #define FG_DEBUG_0 0x500 |
| #define FG_DEBUG_1 0x504 |
| #define FG_DEBUG_2 0x508 |
| #define FG_DEBUG_3 0x50c |
| #define FG_DEBUG_4 0x510 |
| #define FG_DEBUG_5 0x514 |
| #define FG_DEBUG_6 0x518 |
| |
| struct fg_data { |
| }; |
| |
| static const struct fg_data mt6893_fg_data = { |
| }; |
| |
| struct mml_comp_fg { |
| struct mml_comp comp; |
| const struct fg_data *data; |
| }; |
| |
| /* meta data for each different frame config */ |
| struct fg_frame_data { |
| u8 out_idx; |
| }; |
| |
| #define fg_frm_data(i) ((struct fg_frame_data *)(i->data)) |
| |
| static s32 fg_prepare(struct mml_comp *comp, struct mml_task *task, |
| struct mml_comp_config *ccfg) |
| { |
| struct fg_frame_data *fg_frm; |
| |
| fg_frm = kzalloc(sizeof(*fg_frm), GFP_KERNEL); |
| ccfg->data = fg_frm; |
| /* cache out index for easy use */ |
| fg_frm->out_idx = ccfg->node->out_idx; |
| |
| return 0; |
| } |
| |
| static s32 fg_tile_prepare(struct mml_comp *comp, struct mml_task *task, |
| struct mml_comp_config *ccfg, |
| struct tile_func_block *func, |
| union mml_tile_data *data) |
| { |
| struct fg_frame_data *fg_frm = fg_frm_data(ccfg); |
| struct mml_frame_config *cfg = task->config; |
| struct mml_frame_data *src = &cfg->info.src; |
| struct mml_frame_dest *dest = &cfg->info.dest[fg_frm->out_idx]; |
| |
| func->enable_flag = true; |
| |
| if (cfg->info.dest_cnt == 1 && |
| (dest->crop.r.width != src->width || |
| dest->crop.r.height != src->height)) { |
| u32 in_crop_w, in_crop_h; |
| |
| in_crop_w = dest->crop.r.width; |
| in_crop_h = dest->crop.r.height; |
| if (in_crop_w + dest->crop.r.left > src->width) |
| in_crop_w = src->width - dest->crop.r.left; |
| if (in_crop_h + dest->crop.r.top > src->height) |
| in_crop_h = src->height - dest->crop.r.top; |
| func->full_size_x_in = in_crop_w + dest->crop.r.left; |
| func->full_size_y_in = in_crop_h + dest->crop.r.top; |
| } else { |
| func->full_size_x_in = src->width; |
| func->full_size_y_in = src->height; |
| } |
| func->full_size_x_out = func->full_size_x_in; |
| func->full_size_y_out = func->full_size_y_in; |
| |
| return 0; |
| } |
| |
| static const struct mml_comp_tile_ops fg_tile_ops = { |
| .prepare = fg_tile_prepare, |
| }; |
| |
| static s32 fg_init(struct mml_comp *comp, struct mml_task *task, |
| struct mml_comp_config *ccfg) |
| { |
| struct cmdq_pkt *pkt = task->pkts[ccfg->pipe]; |
| const phys_addr_t base_pa = comp->base_pa; |
| |
| cmdq_pkt_write(pkt, NULL, base_pa + FG_TRIGGER, 0, U32_MAX); |
| return 0; |
| } |
| |
| static s32 fg_config_frame(struct mml_comp *comp, struct mml_task *task, |
| struct mml_comp_config *ccfg) |
| { |
| struct cmdq_pkt *pkt = task->pkts[ccfg->pipe]; |
| const phys_addr_t base_pa = comp->base_pa; |
| |
| /* relay mode */ |
| cmdq_pkt_write(pkt, NULL, base_pa + FG_CTRL_0, 1, U32_MAX); |
| cmdq_pkt_write(pkt, NULL, base_pa + FG_CK_EN, 0x7, U32_MAX); |
| |
| return 0; |
| } |
| |
| static s32 fg_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 width = tile->in.xe - tile->in.xs + 1; |
| u32 height = tile->in.ye - tile->in.ys + 1; |
| |
| cmdq_pkt_write(pkt, NULL, base_pa + FG_TILE_INFO_0, |
| (tile->in.xs & 0xffff) + ((width & 0xffff) << 16), U32_MAX); |
| cmdq_pkt_write(pkt, NULL, base_pa + FG_TILE_INFO_1, |
| (tile->in.ys & 0xffff) + ((height & 0xffff) << 16), U32_MAX); |
| return 0; |
| } |
| |
| static const struct mml_comp_config_ops fg_cfg_ops = { |
| .prepare = fg_prepare, |
| .init = fg_init, |
| .frame = fg_config_frame, |
| .tile = fg_config_tile, |
| }; |
| |
| static void fg_debug_dump(struct mml_comp *comp) |
| { |
| void __iomem *base = comp->base; |
| u32 value[16]; |
| |
| mml_err("fg component %u dump:", comp->id); |
| |
| value[0] = readl(base + FG_TRIGGER); |
| value[1] = readl(base + FG_STATUS); |
| value[2] = readl(base + FG_CTRL_0); |
| value[3] = readl(base + FG_CK_EN); |
| value[4] = readl(base + FG_BACK_DOOR_0); |
| value[5] = readl(base + FG_PIC_INFO_0); |
| value[6] = readl(base + FG_PIC_INFO_1); |
| value[7] = readl(base + FG_TILE_INFO_0); |
| value[8] = readl(base + FG_TILE_INFO_1); |
| value[9] = readl(base + FG_DEBUG_0); |
| value[10] = readl(base + FG_DEBUG_1); |
| value[11] = readl(base + FG_DEBUG_2); |
| value[12] = readl(base + FG_DEBUG_3); |
| value[13] = readl(base + FG_DEBUG_4); |
| value[14] = readl(base + FG_DEBUG_5); |
| value[15] = readl(base + FG_DEBUG_6); |
| |
| mml_err("FG_TRIGGER %#010x FG_STATUS %#010x", |
| value[0], value[1]); |
| mml_err("FG_CTRL_0 %#010x FG_CK_EN %#010x", |
| value[2], value[3]); |
| mml_err("FG_BACK_DOOR_0 %#010x FG_PIC_INFO_0 %#010x FG_PIC_INFO_1 %#010x", |
| value[4], value[5], value[6]); |
| mml_err("FG_TILE_INFO_0 %#010x FG_TILE_INFO_1 %#010x", |
| value[7], value[8]); |
| mml_err("FG_DEBUG_0 %#010x FG_DEBUG_1 %#010x FG_DEBUG_2 %#010x", |
| value[9], value[10], value[11]); |
| mml_err("FG_DEBUG_3 %#010x FG_DEBUG_4 %#010x FG_DEBUG_5 %#010x", |
| value[12], value[13], value[14]); |
| mml_err("FG_DEBUG_6 %#010x ", value[15]); |
| } |
| |
| static const struct mml_comp_debug_ops fg_debug_ops = { |
| .dump = &fg_debug_dump, |
| }; |
| |
| static int mml_bind(struct device *dev, struct device *master, void *data) |
| { |
| struct mml_comp_fg *fg = dev_get_drvdata(dev); |
| s32 ret; |
| |
| ret = mml_register_comp(master, &fg->comp); |
| if (ret) |
| dev_err(dev, "Failed to register mml component %s: %d\n", |
| dev->of_node->full_name, ret); |
| return ret; |
| } |
| |
| static void mml_unbind(struct device *dev, struct device *master, void *data) |
| { |
| struct mml_comp_fg *fg = dev_get_drvdata(dev); |
| |
| mml_unregister_comp(master, &fg->comp); |
| } |
| |
| static const struct component_ops mml_comp_ops = { |
| .bind = mml_bind, |
| .unbind = mml_unbind, |
| }; |
| |
| static struct mml_comp_fg *dbg_probed_components[2]; |
| static int dbg_probed_count; |
| |
| static int probe(struct platform_device *pdev) |
| { |
| struct device *dev = &pdev->dev; |
| struct mml_comp_fg *priv; |
| s32 ret; |
| |
| 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 = &fg_tile_ops; |
| priv->comp.config_ops = &fg_cfg_ops; |
| priv->comp.debug_ops = &fg_debug_ops; |
| |
| dbg_probed_components[dbg_probed_count++] = priv; |
| |
| 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); |
| return 0; |
| } |
| |
| const struct of_device_id mml_fg_driver_dt_match[] = { |
| { |
| .compatible = "mediatek,mt6893-mml_fg", |
| .data = &mt6893_fg_data |
| }, |
| {}, |
| }; |
| MODULE_DEVICE_TABLE(of, mml_fg_driver_dt_match); |
| |
| struct platform_driver mml_fg_driver = { |
| .probe = probe, |
| .remove = remove, |
| .driver = { |
| .name = "mediatek-mml-fg", |
| .owner = THIS_MODULE, |
| .of_match_table = mml_fg_driver_dt_match, |
| }, |
| }; |
| |
| //module_platform_driver(mml_fg_driver); |
| |
| static s32 ut_case; |
| static s32 ut_set(const char *val, const struct kernel_param *kp) |
| { |
| s32 result; |
| |
| result = sscanf(val, "%d", &ut_case); |
| if (result != 1) { |
| mml_err("invalid input: %s, result(%d)", val, result); |
| return -EINVAL; |
| } |
| mml_log("%s: case_id=%d", __func__, ut_case); |
| |
| switch (ut_case) { |
| case 0: |
| mml_log("use read to dump current pwm setting"); |
| break; |
| default: |
| mml_err("invalid case_id: %d", ut_case); |
| break; |
| } |
| |
| mml_log("%s END", __func__); |
| return 0; |
| } |
| |
| static s32 ut_get(char *buf, const struct kernel_param *kp) |
| { |
| s32 length = 0; |
| u32 i; |
| |
| switch (ut_case) { |
| case 0: |
| length += snprintf(buf + length, PAGE_SIZE - length, |
| "[%d] probed count: %d\n", ut_case, dbg_probed_count); |
| for(i = 0; i < dbg_probed_count; i++) { |
| length += snprintf(buf + length, PAGE_SIZE - length, |
| " - [%d] mml_comp_id: %d\n", i, |
| dbg_probed_components[i]->comp.id); |
| length += snprintf(buf + length, PAGE_SIZE - length, |
| " - mml_bound: %d\n", |
| dbg_probed_components[i]->comp.bound); |
| } |
| default: |
| mml_err("not support read for case_id: %d", ut_case); |
| break; |
| } |
| buf[length] = '\0'; |
| |
| return length; |
| } |
| |
| static struct kernel_param_ops up_param_ops = { |
| .set = ut_set, |
| .get = ut_get, |
| }; |
| module_param_cb(fg_ut_case, &up_param_ops, NULL, 0644); |
| MODULE_PARM_DESC(fg_ut_case, "mml fg UT test case"); |
| |
| MODULE_AUTHOR("Dennis-YC Hsieh <dennis-yc.hsieh@mediatek.com>"); |
| MODULE_DESCRIPTION("MediaTek SoC display MML FG driver"); |
| MODULE_LICENSE("GPL v2"); |