/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include "mtk_drm_ddp_comp.h" #include "mtk_drm_drv.h" #include "mtk_fbconfig_kdebug.h" #define CMDQ_MBOX_BUF_LIMIT 16 /* default limit count */ // for FPGA porting, pa 2 va #define DISP_REG_ADDR_BASE 0x14000000 #define DISP_REG_ADDR_MASK 0xffffffl #define DISP_COMP_REG_ADDR_MASK 0xfffl struct client_priv { struct dma_pool *buf_pool; u32 pool_limit; atomic_t buf_cnt; struct workqueue_struct *flushq; }; struct regs_addr_record { void __iomem *regs; resource_size_t regs_pa; struct list_head list; }; struct list_head addr_head; bool hasInit; struct cmdq_base *cmdq_register_device(struct device *dev) { struct cmdq_base *clt_base; struct of_phandle_args spec; u32 vals[2] = {0}, idx; s32 ret; clt_base = devm_kzalloc(dev, sizeof(*clt_base), GFP_KERNEL); if (!clt_base) return NULL; /* parse subsys */ for (idx = 0; idx < ARRAY_SIZE(clt_base->subsys); idx++) { if (of_parse_phandle_with_args(dev->of_node, "gce-subsys", "#gce-subsys-cells", idx, &spec)) break; clt_base->subsys[idx].base = spec.args[0]; clt_base->subsys[idx].id = spec.args[1]; } clt_base->count = idx; /* parse CPR range */ ret = of_property_read_u32_array(dev->of_node, "gce-cpr-range", vals, 2); if (!ret) { clt_base->cpr_base = vals[0] + CMDQ_CPR_STRAT_ID; clt_base->cpr_cnt = vals[1]; cmdq_msg("support cpr:%d count:%d", vals[0], vals[1]); } return clt_base; } EXPORT_SYMBOL(cmdq_register_device); struct cmdq_client *cmdq_mbox_create(struct device *dev, int index) { struct cmdq_client *client; struct client_priv *priv; client = kzalloc(sizeof(*client), GFP_KERNEL); if (!client) return ERR_PTR(-ENOMEM); client->client.dev = dev; client->client.tx_block = false; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { cmdq_mbox_destroy(client); return ERR_PTR(-ENOMEM); } priv->pool_limit = CMDQ_MBOX_BUF_LIMIT; priv->flushq = create_singlethread_workqueue("cmdq_flushq"); client->cl_priv = (void *)priv; mutex_init(&client->chan_mutex); return client; } EXPORT_SYMBOL(cmdq_mbox_create); void cmdq_mbox_stop(struct cmdq_client *cl) { } EXPORT_SYMBOL(cmdq_mbox_stop); void cmdq_mbox_pool_create(struct cmdq_client *cl) { struct client_priv *priv = (struct client_priv *)cl->cl_priv; if (unlikely(priv->buf_pool)) { cmdq_msg("buffer pool already created"); return; } priv->buf_pool = dma_pool_create("cmdq", cl->chan->mbox->dev, CMDQ_BUF_ALLOC_SIZE, 0, 0); } EXPORT_SYMBOL(cmdq_mbox_pool_create); void *cmdq_mbox_buf_alloc(struct device *dev, dma_addr_t *pa_out) { void *va; dma_addr_t pa = 0; va = dma_alloc_coherent(dev, CMDQ_BUF_ALLOC_SIZE, &pa, GFP_KERNEL); if (!va) { cmdq_err("alloc dma buffer fail dev:0x%p", dev); dump_stack(); return NULL; } *pa_out = pa; return va; } void cmdq_mbox_buf_free(struct device *dev, void *va, dma_addr_t pa) { } /* parse event from dts * * Example * * dts: * gce-event-names = "disp_rdma0_sof", * "disp_rdma1_sof", * "mdp_rdma0_sof"; * gce-events = <&gce_mbox CMDQ_EVENT_DISP_RDMA0_SOF>, * <&gce_mbox CMDQ_EVENT_DISP_RDMA1_SOF>, * <&gce_mbox CMDQ_EVENT_MDP_RDMA0_SOF>; * * call: * s32 rdma0_sof_event_id = cmdq_dev_get_event(dev, "disp_rdma0_sof"); */ s32 cmdq_dev_get_event(struct device *dev, const char *name) { s32 index = 0; struct of_phandle_args spec = {0}; s32 result; if (!dev) { cmdq_err("no device node"); return -EINVAL; } index = of_property_match_string(dev->of_node, "gce-event-names", name); if (index < 0) { cmdq_err("no gce-event-names property or no such event:%s", name); return index; } if (of_parse_phandle_with_args(dev->of_node, "gce-events", "#gce-event-cells", index, &spec)) { cmdq_err("can't parse gce-events property"); return -ENODEV; } result = spec.args[0]; of_node_put(spec.np); return result; } void cmdq_mbox_destroy(struct cmdq_client *client) { kfree(client->cl_priv); kfree(client); } EXPORT_SYMBOL(cmdq_mbox_destroy); struct cmdq_pkt *cmdq_pkt_create(struct cmdq_client *client) { struct cmdq_pkt *pkt; pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); if (!IS_ERR_OR_NULL(pkt)) return pkt; return NULL; } EXPORT_SYMBOL(cmdq_pkt_create); void cmdq_pkt_destroy(struct cmdq_pkt *pkt) { struct cmdq_client *client = pkt->cl; if (client) mutex_lock(&client->chan_mutex); kfree(pkt->flush_item); kfree(pkt); if (client) mutex_unlock(&client->chan_mutex); } EXPORT_SYMBOL(cmdq_pkt_destroy); u64 *cmdq_pkt_get_va_by_offset(struct cmdq_pkt *pkt, size_t offset) { size_t offset_remaind = offset; struct cmdq_pkt_buffer *buf; list_for_each_entry(buf, &pkt->buf, list_entry) { if (offset_remaind >= CMDQ_CMD_BUFFER_SIZE) { offset_remaind -= CMDQ_CMD_BUFFER_SIZE; continue; } return (u64 *)(buf->va_base + offset_remaind); } return NULL; } EXPORT_SYMBOL(cmdq_pkt_get_va_by_offset); dma_addr_t cmdq_pkt_get_pa_by_offset(struct cmdq_pkt *pkt, u32 offset) { u32 offset_remaind = offset; struct cmdq_pkt_buffer *buf; list_for_each_entry(buf, &pkt->buf, list_entry) { if (offset_remaind >= CMDQ_CMD_BUFFER_SIZE) { offset_remaind -= CMDQ_CMD_BUFFER_SIZE; continue; } return buf->pa_base + offset_remaind; } return 0; } EXPORT_SYMBOL(cmdq_pkt_get_pa_by_offset); s32 cmdq_pkt_read(struct cmdq_pkt *pkt, struct cmdq_base *clt_base, dma_addr_t src_addr, u16 dst_reg_idx) { return 0; } EXPORT_SYMBOL(cmdq_pkt_read); s32 cmdq_pkt_write_reg_addr(struct cmdq_pkt *pkt, dma_addr_t addr, u16 src_reg_idx, u32 mask) { return 0; } EXPORT_SYMBOL(cmdq_pkt_write_reg_addr); s32 cmdq_pkt_write(struct cmdq_pkt *pkt, struct cmdq_base *clt_base, dma_addr_t addr, u32 value, u32 mask) { unsigned long pa = (unsigned long) addr; void __iomem *va = 0; struct regs_addr_record *regs_addr; unsigned int i = 0; struct mtk_drm_private *private = drm_dev->dev_private; struct mtk_ddp_comp *comp; if (DISP_REG_ADDR_BASE != (pa & (~DISP_REG_ADDR_MASK))) { DDPDBG("%s error pa:%x, addr:%x\n", __func__, pa, addr); return -1; } // get va from comp for (i = 0; i < DDP_COMPONENT_ID_MAX; i++) { if (!private->ddp_comp[i]) continue; comp = private->ddp_comp[i]; if (comp->regs_pa == (pa & (~DISP_COMP_REG_ADDR_MASK))) { va = comp->regs + (pa & DISP_COMP_REG_ADDR_MASK); writel(value, va); return 0; } } //remap pa to va if (!hasInit) { INIT_LIST_HEAD(&addr_head); hasInit = true; } list_for_each_entry(regs_addr, &addr_head, list) { if (regs_addr && regs_addr->regs_pa == pa) { va = regs_addr->regs; break; } } if (!va) { va = ioremap_nocache(pa, sizeof(va)); regs_addr = kzalloc(sizeof(struct repaint_job_t), GFP_KERNEL); if (!IS_ERR_OR_NULL(regs_addr)) { regs_addr->regs = va; regs_addr->regs_pa = pa; list_add_tail(®s_addr->list, &addr_head); } DDPDBG("%s:%x, va:%u\n", __func__, pa, va); } writel(value, va); return 0; } EXPORT_SYMBOL(cmdq_pkt_write); s32 cmdq_pkt_mem_move(struct cmdq_pkt *pkt, struct cmdq_base *clt_base, dma_addr_t src_addr, dma_addr_t dst_addr, u16 swap_reg_idx) { return 0; } EXPORT_SYMBOL(cmdq_pkt_mem_move); s32 cmdq_pkt_assign_command(struct cmdq_pkt *pkt, u16 reg_idx, u32 value) { return 0; } EXPORT_SYMBOL(cmdq_pkt_assign_command); s32 cmdq_pkt_logic_command(struct cmdq_pkt *pkt, enum CMDQ_LOGIC_ENUM s_op, u16 result_reg_idx, struct cmdq_operand *left_operand, struct cmdq_operand *right_operand) { return 0; } EXPORT_SYMBOL(cmdq_pkt_logic_command); s32 cmdq_pkt_cond_jump_abs(struct cmdq_pkt *pkt, u16 addr_reg_idx, struct cmdq_operand *left_operand, struct cmdq_operand *right_operand, enum CMDQ_CONDITION_ENUM condition_operator) { return 0; } EXPORT_SYMBOL(cmdq_pkt_cond_jump_abs); s32 cmdq_pkt_poll_reg(struct cmdq_pkt *pkt, u32 value, u8 subsys, u16 offset, u32 mask) { return 0; } EXPORT_SYMBOL(cmdq_pkt_poll_reg); s32 cmdq_pkt_poll_timeout(struct cmdq_pkt *pkt, u32 value, u8 subsys, phys_addr_t addr, u32 mask, u16 count, u16 reg_gpr) { return 0; } EXPORT_SYMBOL(cmdq_pkt_poll_timeout); int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u16 event) { return 0; } EXPORT_SYMBOL(cmdq_pkt_wfe); int cmdq_pkt_wait_no_clear(struct cmdq_pkt *pkt, u16 event) { return 0; } EXPORT_SYMBOL(cmdq_pkt_wait_no_clear); s32 cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u16 event) { return 0; } EXPORT_SYMBOL(cmdq_pkt_clear_event); s32 cmdq_pkt_set_event(struct cmdq_pkt *pkt, u16 event) { return 0; } EXPORT_SYMBOL(cmdq_pkt_set_event); s32 cmdq_pkt_finalize_loop(struct cmdq_pkt *pkt) { return 0; } EXPORT_SYMBOL(cmdq_pkt_finalize_loop); s32 cmdq_pkt_flush_async(struct cmdq_pkt *pkt, cmdq_async_flush_cb cb, void *data) { return 0; } EXPORT_SYMBOL(cmdq_pkt_flush_async); s32 cmdq_pkt_flush_threaded(struct cmdq_pkt *pkt, cmdq_async_flush_cb cb, void *data) { return 0; } EXPORT_SYMBOL(cmdq_pkt_flush_threaded); int cmdq_pkt_flush(struct cmdq_pkt *pkt) { return 0; } EXPORT_SYMBOL(cmdq_pkt_flush);