18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2008 48c2ecf20Sopenharmony_ci * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/list.h> 168c2ecf20Sopenharmony_ci#include <linux/clk.h> 178c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 188c2ecf20Sopenharmony_ci#include <linux/string.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/io.h> 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/dma/ipu-dma.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "../dmaengine.h" 258c2ecf20Sopenharmony_ci#include "ipu_intern.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define FS_VF_IN_VALID 0x00000002 288c2ecf20Sopenharmony_ci#define FS_ENC_IN_VALID 0x00000001 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int ipu_disable_channel(struct idmac *idmac, struct idmac_channel *ichan, 318c2ecf20Sopenharmony_ci bool wait_for_stop); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * There can be only one, we could allocate it dynamically, but then we'd have 358c2ecf20Sopenharmony_ci * to add an extra parameter to some functions, and use something as ugly as 368c2ecf20Sopenharmony_ci * struct ipu *ipu = to_ipu(to_idmac(ichan->dma_chan.device)); 378c2ecf20Sopenharmony_ci * in the ISR 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistatic struct ipu ipu_data; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define to_ipu(id) container_of(id, struct ipu, idmac) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic u32 __idmac_read_icreg(struct ipu *ipu, unsigned long reg) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci return __raw_readl(ipu->reg_ic + reg); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define idmac_read_icreg(ipu, reg) __idmac_read_icreg(ipu, reg - IC_CONF) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic void __idmac_write_icreg(struct ipu *ipu, u32 value, unsigned long reg) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci __raw_writel(value, ipu->reg_ic + reg); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define idmac_write_icreg(ipu, v, reg) __idmac_write_icreg(ipu, v, reg - IC_CONF) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic u32 idmac_read_ipureg(struct ipu *ipu, unsigned long reg) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci return __raw_readl(ipu->reg_ipu + reg); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic void idmac_write_ipureg(struct ipu *ipu, u32 value, unsigned long reg) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci __raw_writel(value, ipu->reg_ipu + reg); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/***************************************************************************** 688c2ecf20Sopenharmony_ci * IPU / IC common functions 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_cistatic void dump_idmac_reg(struct ipu *ipu) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "IDMAC_CONF 0x%x, IC_CONF 0x%x, IDMAC_CHA_EN 0x%x, " 738c2ecf20Sopenharmony_ci "IDMAC_CHA_PRI 0x%x, IDMAC_CHA_BUSY 0x%x\n", 748c2ecf20Sopenharmony_ci idmac_read_icreg(ipu, IDMAC_CONF), 758c2ecf20Sopenharmony_ci idmac_read_icreg(ipu, IC_CONF), 768c2ecf20Sopenharmony_ci idmac_read_icreg(ipu, IDMAC_CHA_EN), 778c2ecf20Sopenharmony_ci idmac_read_icreg(ipu, IDMAC_CHA_PRI), 788c2ecf20Sopenharmony_ci idmac_read_icreg(ipu, IDMAC_CHA_BUSY)); 798c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "BUF0_RDY 0x%x, BUF1_RDY 0x%x, CUR_BUF 0x%x, " 808c2ecf20Sopenharmony_ci "DB_MODE 0x%x, TASKS_STAT 0x%x\n", 818c2ecf20Sopenharmony_ci idmac_read_ipureg(ipu, IPU_CHA_BUF0_RDY), 828c2ecf20Sopenharmony_ci idmac_read_ipureg(ipu, IPU_CHA_BUF1_RDY), 838c2ecf20Sopenharmony_ci idmac_read_ipureg(ipu, IPU_CHA_CUR_BUF), 848c2ecf20Sopenharmony_ci idmac_read_ipureg(ipu, IPU_CHA_DB_MODE_SEL), 858c2ecf20Sopenharmony_ci idmac_read_ipureg(ipu, IPU_TASKS_STAT)); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic uint32_t bytes_per_pixel(enum pixel_fmt fmt) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci switch (fmt) { 918c2ecf20Sopenharmony_ci case IPU_PIX_FMT_GENERIC: /* generic data */ 928c2ecf20Sopenharmony_ci case IPU_PIX_FMT_RGB332: 938c2ecf20Sopenharmony_ci case IPU_PIX_FMT_YUV420P: 948c2ecf20Sopenharmony_ci case IPU_PIX_FMT_YUV422P: 958c2ecf20Sopenharmony_ci default: 968c2ecf20Sopenharmony_ci return 1; 978c2ecf20Sopenharmony_ci case IPU_PIX_FMT_RGB565: 988c2ecf20Sopenharmony_ci case IPU_PIX_FMT_YUYV: 998c2ecf20Sopenharmony_ci case IPU_PIX_FMT_UYVY: 1008c2ecf20Sopenharmony_ci return 2; 1018c2ecf20Sopenharmony_ci case IPU_PIX_FMT_BGR24: 1028c2ecf20Sopenharmony_ci case IPU_PIX_FMT_RGB24: 1038c2ecf20Sopenharmony_ci return 3; 1048c2ecf20Sopenharmony_ci case IPU_PIX_FMT_GENERIC_32: /* generic data */ 1058c2ecf20Sopenharmony_ci case IPU_PIX_FMT_BGR32: 1068c2ecf20Sopenharmony_ci case IPU_PIX_FMT_RGB32: 1078c2ecf20Sopenharmony_ci case IPU_PIX_FMT_ABGR32: 1088c2ecf20Sopenharmony_ci return 4; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* Enable direct write to memory by the Camera Sensor Interface */ 1138c2ecf20Sopenharmony_cistatic void ipu_ic_enable_task(struct ipu *ipu, enum ipu_channel channel) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci uint32_t ic_conf, mask; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci switch (channel) { 1188c2ecf20Sopenharmony_ci case IDMAC_IC_0: 1198c2ecf20Sopenharmony_ci mask = IC_CONF_PRPENC_EN; 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci case IDMAC_IC_7: 1228c2ecf20Sopenharmony_ci mask = IC_CONF_RWS_EN | IC_CONF_PRPENC_EN; 1238c2ecf20Sopenharmony_ci break; 1248c2ecf20Sopenharmony_ci default: 1258c2ecf20Sopenharmony_ci return; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci ic_conf = idmac_read_icreg(ipu, IC_CONF) | mask; 1288c2ecf20Sopenharmony_ci idmac_write_icreg(ipu, ic_conf, IC_CONF); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* Called under spin_lock_irqsave(&ipu_data.lock) */ 1328c2ecf20Sopenharmony_cistatic void ipu_ic_disable_task(struct ipu *ipu, enum ipu_channel channel) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci uint32_t ic_conf, mask; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci switch (channel) { 1378c2ecf20Sopenharmony_ci case IDMAC_IC_0: 1388c2ecf20Sopenharmony_ci mask = IC_CONF_PRPENC_EN; 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci case IDMAC_IC_7: 1418c2ecf20Sopenharmony_ci mask = IC_CONF_RWS_EN | IC_CONF_PRPENC_EN; 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci default: 1448c2ecf20Sopenharmony_ci return; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci ic_conf = idmac_read_icreg(ipu, IC_CONF) & ~mask; 1478c2ecf20Sopenharmony_ci idmac_write_icreg(ipu, ic_conf, IC_CONF); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic uint32_t ipu_channel_status(struct ipu *ipu, enum ipu_channel channel) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci uint32_t stat = TASK_STAT_IDLE; 1538c2ecf20Sopenharmony_ci uint32_t task_stat_reg = idmac_read_ipureg(ipu, IPU_TASKS_STAT); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci switch (channel) { 1568c2ecf20Sopenharmony_ci case IDMAC_IC_7: 1578c2ecf20Sopenharmony_ci stat = (task_stat_reg & TSTAT_CSI2MEM_MASK) >> 1588c2ecf20Sopenharmony_ci TSTAT_CSI2MEM_OFFSET; 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci case IDMAC_IC_0: 1618c2ecf20Sopenharmony_ci case IDMAC_SDC_0: 1628c2ecf20Sopenharmony_ci case IDMAC_SDC_1: 1638c2ecf20Sopenharmony_ci default: 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci return stat; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistruct chan_param_mem_planar { 1708c2ecf20Sopenharmony_ci /* Word 0 */ 1718c2ecf20Sopenharmony_ci u32 xv:10; 1728c2ecf20Sopenharmony_ci u32 yv:10; 1738c2ecf20Sopenharmony_ci u32 xb:12; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci u32 yb:12; 1768c2ecf20Sopenharmony_ci u32 res1:2; 1778c2ecf20Sopenharmony_ci u32 nsb:1; 1788c2ecf20Sopenharmony_ci u32 lnpb:6; 1798c2ecf20Sopenharmony_ci u32 ubo_l:11; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci u32 ubo_h:15; 1828c2ecf20Sopenharmony_ci u32 vbo_l:17; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci u32 vbo_h:9; 1858c2ecf20Sopenharmony_ci u32 res2:3; 1868c2ecf20Sopenharmony_ci u32 fw:12; 1878c2ecf20Sopenharmony_ci u32 fh_l:8; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci u32 fh_h:4; 1908c2ecf20Sopenharmony_ci u32 res3:28; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* Word 1 */ 1938c2ecf20Sopenharmony_ci u32 eba0; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci u32 eba1; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci u32 bpp:3; 1988c2ecf20Sopenharmony_ci u32 sl:14; 1998c2ecf20Sopenharmony_ci u32 pfs:3; 2008c2ecf20Sopenharmony_ci u32 bam:3; 2018c2ecf20Sopenharmony_ci u32 res4:2; 2028c2ecf20Sopenharmony_ci u32 npb:6; 2038c2ecf20Sopenharmony_ci u32 res5:1; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci u32 sat:2; 2068c2ecf20Sopenharmony_ci u32 res6:30; 2078c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistruct chan_param_mem_interleaved { 2108c2ecf20Sopenharmony_ci /* Word 0 */ 2118c2ecf20Sopenharmony_ci u32 xv:10; 2128c2ecf20Sopenharmony_ci u32 yv:10; 2138c2ecf20Sopenharmony_ci u32 xb:12; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci u32 yb:12; 2168c2ecf20Sopenharmony_ci u32 sce:1; 2178c2ecf20Sopenharmony_ci u32 res1:1; 2188c2ecf20Sopenharmony_ci u32 nsb:1; 2198c2ecf20Sopenharmony_ci u32 lnpb:6; 2208c2ecf20Sopenharmony_ci u32 sx:10; 2218c2ecf20Sopenharmony_ci u32 sy_l:1; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci u32 sy_h:9; 2248c2ecf20Sopenharmony_ci u32 ns:10; 2258c2ecf20Sopenharmony_ci u32 sm:10; 2268c2ecf20Sopenharmony_ci u32 sdx_l:3; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci u32 sdx_h:2; 2298c2ecf20Sopenharmony_ci u32 sdy:5; 2308c2ecf20Sopenharmony_ci u32 sdrx:1; 2318c2ecf20Sopenharmony_ci u32 sdry:1; 2328c2ecf20Sopenharmony_ci u32 sdr1:1; 2338c2ecf20Sopenharmony_ci u32 res2:2; 2348c2ecf20Sopenharmony_ci u32 fw:12; 2358c2ecf20Sopenharmony_ci u32 fh_l:8; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci u32 fh_h:4; 2388c2ecf20Sopenharmony_ci u32 res3:28; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* Word 1 */ 2418c2ecf20Sopenharmony_ci u32 eba0; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci u32 eba1; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci u32 bpp:3; 2468c2ecf20Sopenharmony_ci u32 sl:14; 2478c2ecf20Sopenharmony_ci u32 pfs:3; 2488c2ecf20Sopenharmony_ci u32 bam:3; 2498c2ecf20Sopenharmony_ci u32 res4:2; 2508c2ecf20Sopenharmony_ci u32 npb:6; 2518c2ecf20Sopenharmony_ci u32 res5:1; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci u32 sat:2; 2548c2ecf20Sopenharmony_ci u32 scc:1; 2558c2ecf20Sopenharmony_ci u32 ofs0:5; 2568c2ecf20Sopenharmony_ci u32 ofs1:5; 2578c2ecf20Sopenharmony_ci u32 ofs2:5; 2588c2ecf20Sopenharmony_ci u32 ofs3:5; 2598c2ecf20Sopenharmony_ci u32 wid0:3; 2608c2ecf20Sopenharmony_ci u32 wid1:3; 2618c2ecf20Sopenharmony_ci u32 wid2:3; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci u32 wid3:3; 2648c2ecf20Sopenharmony_ci u32 dec_sel:1; 2658c2ecf20Sopenharmony_ci u32 res6:28; 2668c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ciunion chan_param_mem { 2698c2ecf20Sopenharmony_ci struct chan_param_mem_planar pp; 2708c2ecf20Sopenharmony_ci struct chan_param_mem_interleaved ip; 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic void ipu_ch_param_set_plane_offset(union chan_param_mem *params, 2748c2ecf20Sopenharmony_ci u32 u_offset, u32 v_offset) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci params->pp.ubo_l = u_offset & 0x7ff; 2778c2ecf20Sopenharmony_ci params->pp.ubo_h = u_offset >> 11; 2788c2ecf20Sopenharmony_ci params->pp.vbo_l = v_offset & 0x1ffff; 2798c2ecf20Sopenharmony_ci params->pp.vbo_h = v_offset >> 17; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic void ipu_ch_param_set_size(union chan_param_mem *params, 2838c2ecf20Sopenharmony_ci uint32_t pixel_fmt, uint16_t width, 2848c2ecf20Sopenharmony_ci uint16_t height, uint16_t stride) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci u32 u_offset; 2878c2ecf20Sopenharmony_ci u32 v_offset; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci params->pp.fw = width - 1; 2908c2ecf20Sopenharmony_ci params->pp.fh_l = height - 1; 2918c2ecf20Sopenharmony_ci params->pp.fh_h = (height - 1) >> 8; 2928c2ecf20Sopenharmony_ci params->pp.sl = stride - 1; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci switch (pixel_fmt) { 2958c2ecf20Sopenharmony_ci case IPU_PIX_FMT_GENERIC: 2968c2ecf20Sopenharmony_ci /*Represents 8-bit Generic data */ 2978c2ecf20Sopenharmony_ci params->pp.bpp = 3; 2988c2ecf20Sopenharmony_ci params->pp.pfs = 7; 2998c2ecf20Sopenharmony_ci params->pp.npb = 31; 3008c2ecf20Sopenharmony_ci params->pp.sat = 2; /* SAT = use 32-bit access */ 3018c2ecf20Sopenharmony_ci break; 3028c2ecf20Sopenharmony_ci case IPU_PIX_FMT_GENERIC_32: 3038c2ecf20Sopenharmony_ci /*Represents 32-bit Generic data */ 3048c2ecf20Sopenharmony_ci params->pp.bpp = 0; 3058c2ecf20Sopenharmony_ci params->pp.pfs = 7; 3068c2ecf20Sopenharmony_ci params->pp.npb = 7; 3078c2ecf20Sopenharmony_ci params->pp.sat = 2; /* SAT = use 32-bit access */ 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci case IPU_PIX_FMT_RGB565: 3108c2ecf20Sopenharmony_ci params->ip.bpp = 2; 3118c2ecf20Sopenharmony_ci params->ip.pfs = 4; 3128c2ecf20Sopenharmony_ci params->ip.npb = 15; 3138c2ecf20Sopenharmony_ci params->ip.sat = 2; /* SAT = 32-bit access */ 3148c2ecf20Sopenharmony_ci params->ip.ofs0 = 0; /* Red bit offset */ 3158c2ecf20Sopenharmony_ci params->ip.ofs1 = 5; /* Green bit offset */ 3168c2ecf20Sopenharmony_ci params->ip.ofs2 = 11; /* Blue bit offset */ 3178c2ecf20Sopenharmony_ci params->ip.ofs3 = 16; /* Alpha bit offset */ 3188c2ecf20Sopenharmony_ci params->ip.wid0 = 4; /* Red bit width - 1 */ 3198c2ecf20Sopenharmony_ci params->ip.wid1 = 5; /* Green bit width - 1 */ 3208c2ecf20Sopenharmony_ci params->ip.wid2 = 4; /* Blue bit width - 1 */ 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci case IPU_PIX_FMT_BGR24: 3238c2ecf20Sopenharmony_ci params->ip.bpp = 1; /* 24 BPP & RGB PFS */ 3248c2ecf20Sopenharmony_ci params->ip.pfs = 4; 3258c2ecf20Sopenharmony_ci params->ip.npb = 7; 3268c2ecf20Sopenharmony_ci params->ip.sat = 2; /* SAT = 32-bit access */ 3278c2ecf20Sopenharmony_ci params->ip.ofs0 = 0; /* Red bit offset */ 3288c2ecf20Sopenharmony_ci params->ip.ofs1 = 8; /* Green bit offset */ 3298c2ecf20Sopenharmony_ci params->ip.ofs2 = 16; /* Blue bit offset */ 3308c2ecf20Sopenharmony_ci params->ip.ofs3 = 24; /* Alpha bit offset */ 3318c2ecf20Sopenharmony_ci params->ip.wid0 = 7; /* Red bit width - 1 */ 3328c2ecf20Sopenharmony_ci params->ip.wid1 = 7; /* Green bit width - 1 */ 3338c2ecf20Sopenharmony_ci params->ip.wid2 = 7; /* Blue bit width - 1 */ 3348c2ecf20Sopenharmony_ci break; 3358c2ecf20Sopenharmony_ci case IPU_PIX_FMT_RGB24: 3368c2ecf20Sopenharmony_ci params->ip.bpp = 1; /* 24 BPP & RGB PFS */ 3378c2ecf20Sopenharmony_ci params->ip.pfs = 4; 3388c2ecf20Sopenharmony_ci params->ip.npb = 7; 3398c2ecf20Sopenharmony_ci params->ip.sat = 2; /* SAT = 32-bit access */ 3408c2ecf20Sopenharmony_ci params->ip.ofs0 = 16; /* Red bit offset */ 3418c2ecf20Sopenharmony_ci params->ip.ofs1 = 8; /* Green bit offset */ 3428c2ecf20Sopenharmony_ci params->ip.ofs2 = 0; /* Blue bit offset */ 3438c2ecf20Sopenharmony_ci params->ip.ofs3 = 24; /* Alpha bit offset */ 3448c2ecf20Sopenharmony_ci params->ip.wid0 = 7; /* Red bit width - 1 */ 3458c2ecf20Sopenharmony_ci params->ip.wid1 = 7; /* Green bit width - 1 */ 3468c2ecf20Sopenharmony_ci params->ip.wid2 = 7; /* Blue bit width - 1 */ 3478c2ecf20Sopenharmony_ci break; 3488c2ecf20Sopenharmony_ci case IPU_PIX_FMT_BGRA32: 3498c2ecf20Sopenharmony_ci case IPU_PIX_FMT_BGR32: 3508c2ecf20Sopenharmony_ci case IPU_PIX_FMT_ABGR32: 3518c2ecf20Sopenharmony_ci params->ip.bpp = 0; 3528c2ecf20Sopenharmony_ci params->ip.pfs = 4; 3538c2ecf20Sopenharmony_ci params->ip.npb = 7; 3548c2ecf20Sopenharmony_ci params->ip.sat = 2; /* SAT = 32-bit access */ 3558c2ecf20Sopenharmony_ci params->ip.ofs0 = 8; /* Red bit offset */ 3568c2ecf20Sopenharmony_ci params->ip.ofs1 = 16; /* Green bit offset */ 3578c2ecf20Sopenharmony_ci params->ip.ofs2 = 24; /* Blue bit offset */ 3588c2ecf20Sopenharmony_ci params->ip.ofs3 = 0; /* Alpha bit offset */ 3598c2ecf20Sopenharmony_ci params->ip.wid0 = 7; /* Red bit width - 1 */ 3608c2ecf20Sopenharmony_ci params->ip.wid1 = 7; /* Green bit width - 1 */ 3618c2ecf20Sopenharmony_ci params->ip.wid2 = 7; /* Blue bit width - 1 */ 3628c2ecf20Sopenharmony_ci params->ip.wid3 = 7; /* Alpha bit width - 1 */ 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci case IPU_PIX_FMT_RGBA32: 3658c2ecf20Sopenharmony_ci case IPU_PIX_FMT_RGB32: 3668c2ecf20Sopenharmony_ci params->ip.bpp = 0; 3678c2ecf20Sopenharmony_ci params->ip.pfs = 4; 3688c2ecf20Sopenharmony_ci params->ip.npb = 7; 3698c2ecf20Sopenharmony_ci params->ip.sat = 2; /* SAT = 32-bit access */ 3708c2ecf20Sopenharmony_ci params->ip.ofs0 = 24; /* Red bit offset */ 3718c2ecf20Sopenharmony_ci params->ip.ofs1 = 16; /* Green bit offset */ 3728c2ecf20Sopenharmony_ci params->ip.ofs2 = 8; /* Blue bit offset */ 3738c2ecf20Sopenharmony_ci params->ip.ofs3 = 0; /* Alpha bit offset */ 3748c2ecf20Sopenharmony_ci params->ip.wid0 = 7; /* Red bit width - 1 */ 3758c2ecf20Sopenharmony_ci params->ip.wid1 = 7; /* Green bit width - 1 */ 3768c2ecf20Sopenharmony_ci params->ip.wid2 = 7; /* Blue bit width - 1 */ 3778c2ecf20Sopenharmony_ci params->ip.wid3 = 7; /* Alpha bit width - 1 */ 3788c2ecf20Sopenharmony_ci break; 3798c2ecf20Sopenharmony_ci case IPU_PIX_FMT_UYVY: 3808c2ecf20Sopenharmony_ci params->ip.bpp = 2; 3818c2ecf20Sopenharmony_ci params->ip.pfs = 6; 3828c2ecf20Sopenharmony_ci params->ip.npb = 7; 3838c2ecf20Sopenharmony_ci params->ip.sat = 2; /* SAT = 32-bit access */ 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci case IPU_PIX_FMT_YUV420P2: 3868c2ecf20Sopenharmony_ci case IPU_PIX_FMT_YUV420P: 3878c2ecf20Sopenharmony_ci params->ip.bpp = 3; 3888c2ecf20Sopenharmony_ci params->ip.pfs = 3; 3898c2ecf20Sopenharmony_ci params->ip.npb = 7; 3908c2ecf20Sopenharmony_ci params->ip.sat = 2; /* SAT = 32-bit access */ 3918c2ecf20Sopenharmony_ci u_offset = stride * height; 3928c2ecf20Sopenharmony_ci v_offset = u_offset + u_offset / 4; 3938c2ecf20Sopenharmony_ci ipu_ch_param_set_plane_offset(params, u_offset, v_offset); 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci case IPU_PIX_FMT_YVU422P: 3968c2ecf20Sopenharmony_ci params->ip.bpp = 3; 3978c2ecf20Sopenharmony_ci params->ip.pfs = 2; 3988c2ecf20Sopenharmony_ci params->ip.npb = 7; 3998c2ecf20Sopenharmony_ci params->ip.sat = 2; /* SAT = 32-bit access */ 4008c2ecf20Sopenharmony_ci v_offset = stride * height; 4018c2ecf20Sopenharmony_ci u_offset = v_offset + v_offset / 2; 4028c2ecf20Sopenharmony_ci ipu_ch_param_set_plane_offset(params, u_offset, v_offset); 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci case IPU_PIX_FMT_YUV422P: 4058c2ecf20Sopenharmony_ci params->ip.bpp = 3; 4068c2ecf20Sopenharmony_ci params->ip.pfs = 2; 4078c2ecf20Sopenharmony_ci params->ip.npb = 7; 4088c2ecf20Sopenharmony_ci params->ip.sat = 2; /* SAT = 32-bit access */ 4098c2ecf20Sopenharmony_ci u_offset = stride * height; 4108c2ecf20Sopenharmony_ci v_offset = u_offset + u_offset / 2; 4118c2ecf20Sopenharmony_ci ipu_ch_param_set_plane_offset(params, u_offset, v_offset); 4128c2ecf20Sopenharmony_ci break; 4138c2ecf20Sopenharmony_ci default: 4148c2ecf20Sopenharmony_ci dev_err(ipu_data.dev, 4158c2ecf20Sopenharmony_ci "mx3 ipu: unimplemented pixel format %d\n", pixel_fmt); 4168c2ecf20Sopenharmony_ci break; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci params->pp.nsb = 1; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic void ipu_ch_param_set_buffer(union chan_param_mem *params, 4238c2ecf20Sopenharmony_ci dma_addr_t buf0, dma_addr_t buf1) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci params->pp.eba0 = buf0; 4268c2ecf20Sopenharmony_ci params->pp.eba1 = buf1; 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic void ipu_ch_param_set_rotation(union chan_param_mem *params, 4308c2ecf20Sopenharmony_ci enum ipu_rotate_mode rotate) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci params->pp.bam = rotate; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic void ipu_write_param_mem(uint32_t addr, uint32_t *data, 4368c2ecf20Sopenharmony_ci uint32_t num_words) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci for (; num_words > 0; num_words--) { 4398c2ecf20Sopenharmony_ci dev_dbg(ipu_data.dev, 4408c2ecf20Sopenharmony_ci "write param mem - addr = 0x%08X, data = 0x%08X\n", 4418c2ecf20Sopenharmony_ci addr, *data); 4428c2ecf20Sopenharmony_ci idmac_write_ipureg(&ipu_data, addr, IPU_IMA_ADDR); 4438c2ecf20Sopenharmony_ci idmac_write_ipureg(&ipu_data, *data++, IPU_IMA_DATA); 4448c2ecf20Sopenharmony_ci addr++; 4458c2ecf20Sopenharmony_ci if ((addr & 0x7) == 5) { 4468c2ecf20Sopenharmony_ci addr &= ~0x7; /* set to word 0 */ 4478c2ecf20Sopenharmony_ci addr += 8; /* increment to next row */ 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic int calc_resize_coeffs(uint32_t in_size, uint32_t out_size, 4538c2ecf20Sopenharmony_ci uint32_t *resize_coeff, 4548c2ecf20Sopenharmony_ci uint32_t *downsize_coeff) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci uint32_t temp_size; 4578c2ecf20Sopenharmony_ci uint32_t temp_downsize; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci *resize_coeff = 1 << 13; 4608c2ecf20Sopenharmony_ci *downsize_coeff = 1 << 13; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* Cannot downsize more than 8:1 */ 4638c2ecf20Sopenharmony_ci if (out_size << 3 < in_size) 4648c2ecf20Sopenharmony_ci return -EINVAL; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* compute downsizing coefficient */ 4678c2ecf20Sopenharmony_ci temp_downsize = 0; 4688c2ecf20Sopenharmony_ci temp_size = in_size; 4698c2ecf20Sopenharmony_ci while (temp_size >= out_size * 2 && temp_downsize < 2) { 4708c2ecf20Sopenharmony_ci temp_size >>= 1; 4718c2ecf20Sopenharmony_ci temp_downsize++; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci *downsize_coeff = temp_downsize; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* 4768c2ecf20Sopenharmony_ci * compute resizing coefficient using the following formula: 4778c2ecf20Sopenharmony_ci * resize_coeff = M*(SI -1)/(SO - 1) 4788c2ecf20Sopenharmony_ci * where M = 2^13, SI - input size, SO - output size 4798c2ecf20Sopenharmony_ci */ 4808c2ecf20Sopenharmony_ci *resize_coeff = (8192L * (temp_size - 1)) / (out_size - 1); 4818c2ecf20Sopenharmony_ci if (*resize_coeff >= 16384L) { 4828c2ecf20Sopenharmony_ci dev_err(ipu_data.dev, "Warning! Overflow on resize coeff.\n"); 4838c2ecf20Sopenharmony_ci *resize_coeff = 0x3FFF; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci dev_dbg(ipu_data.dev, "resizing from %u -> %u pixels, " 4878c2ecf20Sopenharmony_ci "downsize=%u, resize=%u.%lu (reg=%u)\n", in_size, out_size, 4888c2ecf20Sopenharmony_ci *downsize_coeff, *resize_coeff >= 8192L ? 1 : 0, 4898c2ecf20Sopenharmony_ci ((*resize_coeff & 0x1FFF) * 10000L) / 8192L, *resize_coeff); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci return 0; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic enum ipu_color_space format_to_colorspace(enum pixel_fmt fmt) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci switch (fmt) { 4978c2ecf20Sopenharmony_ci case IPU_PIX_FMT_RGB565: 4988c2ecf20Sopenharmony_ci case IPU_PIX_FMT_BGR24: 4998c2ecf20Sopenharmony_ci case IPU_PIX_FMT_RGB24: 5008c2ecf20Sopenharmony_ci case IPU_PIX_FMT_BGR32: 5018c2ecf20Sopenharmony_ci case IPU_PIX_FMT_RGB32: 5028c2ecf20Sopenharmony_ci return IPU_COLORSPACE_RGB; 5038c2ecf20Sopenharmony_ci default: 5048c2ecf20Sopenharmony_ci return IPU_COLORSPACE_YCBCR; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic int ipu_ic_init_prpenc(struct ipu *ipu, 5098c2ecf20Sopenharmony_ci union ipu_channel_param *params, bool src_is_csi) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci uint32_t reg, ic_conf; 5128c2ecf20Sopenharmony_ci uint32_t downsize_coeff, resize_coeff; 5138c2ecf20Sopenharmony_ci enum ipu_color_space in_fmt, out_fmt; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* Setup vertical resizing */ 5168c2ecf20Sopenharmony_ci calc_resize_coeffs(params->video.in_height, 5178c2ecf20Sopenharmony_ci params->video.out_height, 5188c2ecf20Sopenharmony_ci &resize_coeff, &downsize_coeff); 5198c2ecf20Sopenharmony_ci reg = (downsize_coeff << 30) | (resize_coeff << 16); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci /* Setup horizontal resizing */ 5228c2ecf20Sopenharmony_ci calc_resize_coeffs(params->video.in_width, 5238c2ecf20Sopenharmony_ci params->video.out_width, 5248c2ecf20Sopenharmony_ci &resize_coeff, &downsize_coeff); 5258c2ecf20Sopenharmony_ci reg |= (downsize_coeff << 14) | resize_coeff; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* Setup color space conversion */ 5288c2ecf20Sopenharmony_ci in_fmt = format_to_colorspace(params->video.in_pixel_fmt); 5298c2ecf20Sopenharmony_ci out_fmt = format_to_colorspace(params->video.out_pixel_fmt); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* 5328c2ecf20Sopenharmony_ci * Colourspace conversion unsupported yet - see _init_csc() in 5338c2ecf20Sopenharmony_ci * Freescale sources 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_ci if (in_fmt != out_fmt) { 5368c2ecf20Sopenharmony_ci dev_err(ipu->dev, "Colourspace conversion unsupported!\n"); 5378c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci idmac_write_icreg(ipu, reg, IC_PRP_ENC_RSC); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci ic_conf = idmac_read_icreg(ipu, IC_CONF); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (src_is_csi) 5458c2ecf20Sopenharmony_ci ic_conf &= ~IC_CONF_RWS_EN; 5468c2ecf20Sopenharmony_ci else 5478c2ecf20Sopenharmony_ci ic_conf |= IC_CONF_RWS_EN; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci idmac_write_icreg(ipu, ic_conf, IC_CONF); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return 0; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic uint32_t dma_param_addr(uint32_t dma_ch) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci /* Channel Parameter Memory */ 5578c2ecf20Sopenharmony_ci return 0x10000 | (dma_ch << 4); 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic void ipu_channel_set_priority(struct ipu *ipu, enum ipu_channel channel, 5618c2ecf20Sopenharmony_ci bool prio) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci u32 reg = idmac_read_icreg(ipu, IDMAC_CHA_PRI); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (prio) 5668c2ecf20Sopenharmony_ci reg |= 1UL << channel; 5678c2ecf20Sopenharmony_ci else 5688c2ecf20Sopenharmony_ci reg &= ~(1UL << channel); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci idmac_write_icreg(ipu, reg, IDMAC_CHA_PRI); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci dump_idmac_reg(ipu); 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic uint32_t ipu_channel_conf_mask(enum ipu_channel channel) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci uint32_t mask; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci switch (channel) { 5808c2ecf20Sopenharmony_ci case IDMAC_IC_0: 5818c2ecf20Sopenharmony_ci case IDMAC_IC_7: 5828c2ecf20Sopenharmony_ci mask = IPU_CONF_CSI_EN | IPU_CONF_IC_EN; 5838c2ecf20Sopenharmony_ci break; 5848c2ecf20Sopenharmony_ci case IDMAC_SDC_0: 5858c2ecf20Sopenharmony_ci case IDMAC_SDC_1: 5868c2ecf20Sopenharmony_ci mask = IPU_CONF_SDC_EN | IPU_CONF_DI_EN; 5878c2ecf20Sopenharmony_ci break; 5888c2ecf20Sopenharmony_ci default: 5898c2ecf20Sopenharmony_ci mask = 0; 5908c2ecf20Sopenharmony_ci break; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci return mask; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci/** 5978c2ecf20Sopenharmony_ci * ipu_enable_channel() - enable an IPU channel. 5988c2ecf20Sopenharmony_ci * @idmac: IPU DMAC context. 5998c2ecf20Sopenharmony_ci * @ichan: IDMAC channel. 6008c2ecf20Sopenharmony_ci * @return: 0 on success or negative error code on failure. 6018c2ecf20Sopenharmony_ci */ 6028c2ecf20Sopenharmony_cistatic int ipu_enable_channel(struct idmac *idmac, struct idmac_channel *ichan) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci struct ipu *ipu = to_ipu(idmac); 6058c2ecf20Sopenharmony_ci enum ipu_channel channel = ichan->dma_chan.chan_id; 6068c2ecf20Sopenharmony_ci uint32_t reg; 6078c2ecf20Sopenharmony_ci unsigned long flags; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* Reset to buffer 0 */ 6128c2ecf20Sopenharmony_ci idmac_write_ipureg(ipu, 1UL << channel, IPU_CHA_CUR_BUF); 6138c2ecf20Sopenharmony_ci ichan->active_buffer = 0; 6148c2ecf20Sopenharmony_ci ichan->status = IPU_CHANNEL_ENABLED; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci switch (channel) { 6178c2ecf20Sopenharmony_ci case IDMAC_SDC_0: 6188c2ecf20Sopenharmony_ci case IDMAC_SDC_1: 6198c2ecf20Sopenharmony_ci case IDMAC_IC_7: 6208c2ecf20Sopenharmony_ci ipu_channel_set_priority(ipu, channel, true); 6218c2ecf20Sopenharmony_ci default: 6228c2ecf20Sopenharmony_ci break; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci reg = idmac_read_icreg(ipu, IDMAC_CHA_EN); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci idmac_write_icreg(ipu, reg | (1UL << channel), IDMAC_CHA_EN); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci ipu_ic_enable_task(ipu, channel); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 6328c2ecf20Sopenharmony_ci return 0; 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci/** 6368c2ecf20Sopenharmony_ci * ipu_init_channel_buffer() - initialize a buffer for logical IPU channel. 6378c2ecf20Sopenharmony_ci * @ichan: IDMAC channel. 6388c2ecf20Sopenharmony_ci * @pixel_fmt: pixel format of buffer. Pixel format is a FOURCC ASCII code. 6398c2ecf20Sopenharmony_ci * @width: width of buffer in pixels. 6408c2ecf20Sopenharmony_ci * @height: height of buffer in pixels. 6418c2ecf20Sopenharmony_ci * @stride: stride length of buffer in pixels. 6428c2ecf20Sopenharmony_ci * @rot_mode: rotation mode of buffer. A rotation setting other than 6438c2ecf20Sopenharmony_ci * IPU_ROTATE_VERT_FLIP should only be used for input buffers of 6448c2ecf20Sopenharmony_ci * rotation channels. 6458c2ecf20Sopenharmony_ci * @phyaddr_0: buffer 0 physical address. 6468c2ecf20Sopenharmony_ci * @phyaddr_1: buffer 1 physical address. Setting this to a value other than 6478c2ecf20Sopenharmony_ci * NULL enables double buffering mode. 6488c2ecf20Sopenharmony_ci * @return: 0 on success or negative error code on failure. 6498c2ecf20Sopenharmony_ci */ 6508c2ecf20Sopenharmony_cistatic int ipu_init_channel_buffer(struct idmac_channel *ichan, 6518c2ecf20Sopenharmony_ci enum pixel_fmt pixel_fmt, 6528c2ecf20Sopenharmony_ci uint16_t width, uint16_t height, 6538c2ecf20Sopenharmony_ci uint32_t stride, 6548c2ecf20Sopenharmony_ci enum ipu_rotate_mode rot_mode, 6558c2ecf20Sopenharmony_ci dma_addr_t phyaddr_0, dma_addr_t phyaddr_1) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci enum ipu_channel channel = ichan->dma_chan.chan_id; 6588c2ecf20Sopenharmony_ci struct idmac *idmac = to_idmac(ichan->dma_chan.device); 6598c2ecf20Sopenharmony_ci struct ipu *ipu = to_ipu(idmac); 6608c2ecf20Sopenharmony_ci union chan_param_mem params = {}; 6618c2ecf20Sopenharmony_ci unsigned long flags; 6628c2ecf20Sopenharmony_ci uint32_t reg; 6638c2ecf20Sopenharmony_ci uint32_t stride_bytes; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci stride_bytes = stride * bytes_per_pixel(pixel_fmt); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (stride_bytes % 4) { 6688c2ecf20Sopenharmony_ci dev_err(ipu->dev, 6698c2ecf20Sopenharmony_ci "Stride length must be 32-bit aligned, stride = %d, bytes = %d\n", 6708c2ecf20Sopenharmony_ci stride, stride_bytes); 6718c2ecf20Sopenharmony_ci return -EINVAL; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci /* IC channel's stride must be a multiple of 8 pixels */ 6758c2ecf20Sopenharmony_ci if ((channel <= IDMAC_IC_13) && (stride % 8)) { 6768c2ecf20Sopenharmony_ci dev_err(ipu->dev, "Stride must be 8 pixel multiple\n"); 6778c2ecf20Sopenharmony_ci return -EINVAL; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci /* Build parameter memory data for DMA channel */ 6818c2ecf20Sopenharmony_ci ipu_ch_param_set_size(¶ms, pixel_fmt, width, height, stride_bytes); 6828c2ecf20Sopenharmony_ci ipu_ch_param_set_buffer(¶ms, phyaddr_0, phyaddr_1); 6838c2ecf20Sopenharmony_ci ipu_ch_param_set_rotation(¶ms, rot_mode); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci ipu_write_param_mem(dma_param_addr(channel), (uint32_t *)¶ms, 10); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci reg = idmac_read_ipureg(ipu, IPU_CHA_DB_MODE_SEL); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (phyaddr_1) 6928c2ecf20Sopenharmony_ci reg |= 1UL << channel; 6938c2ecf20Sopenharmony_ci else 6948c2ecf20Sopenharmony_ci reg &= ~(1UL << channel); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci idmac_write_ipureg(ipu, reg, IPU_CHA_DB_MODE_SEL); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci ichan->status = IPU_CHANNEL_READY; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci return 0; 7038c2ecf20Sopenharmony_ci} 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci/** 7068c2ecf20Sopenharmony_ci * ipu_select_buffer() - mark a channel's buffer as ready. 7078c2ecf20Sopenharmony_ci * @channel: channel ID. 7088c2ecf20Sopenharmony_ci * @buffer_n: buffer number to mark ready. 7098c2ecf20Sopenharmony_ci */ 7108c2ecf20Sopenharmony_cistatic void ipu_select_buffer(enum ipu_channel channel, int buffer_n) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci /* No locking - this is a write-one-to-set register, cleared by IPU */ 7138c2ecf20Sopenharmony_ci if (buffer_n == 0) 7148c2ecf20Sopenharmony_ci /* Mark buffer 0 as ready. */ 7158c2ecf20Sopenharmony_ci idmac_write_ipureg(&ipu_data, 1UL << channel, IPU_CHA_BUF0_RDY); 7168c2ecf20Sopenharmony_ci else 7178c2ecf20Sopenharmony_ci /* Mark buffer 1 as ready. */ 7188c2ecf20Sopenharmony_ci idmac_write_ipureg(&ipu_data, 1UL << channel, IPU_CHA_BUF1_RDY); 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci/** 7228c2ecf20Sopenharmony_ci * ipu_update_channel_buffer() - update physical address of a channel buffer. 7238c2ecf20Sopenharmony_ci * @ichan: IDMAC channel. 7248c2ecf20Sopenharmony_ci * @buffer_n: buffer number to update. 7258c2ecf20Sopenharmony_ci * 0 or 1 are the only valid values. 7268c2ecf20Sopenharmony_ci * @phyaddr: buffer physical address. 7278c2ecf20Sopenharmony_ci */ 7288c2ecf20Sopenharmony_ci/* Called under spin_lock(_irqsave)(&ichan->lock) */ 7298c2ecf20Sopenharmony_cistatic void ipu_update_channel_buffer(struct idmac_channel *ichan, 7308c2ecf20Sopenharmony_ci int buffer_n, dma_addr_t phyaddr) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci enum ipu_channel channel = ichan->dma_chan.chan_id; 7338c2ecf20Sopenharmony_ci uint32_t reg; 7348c2ecf20Sopenharmony_ci unsigned long flags; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu_data.lock, flags); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci if (buffer_n == 0) { 7398c2ecf20Sopenharmony_ci reg = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF0_RDY); 7408c2ecf20Sopenharmony_ci if (reg & (1UL << channel)) { 7418c2ecf20Sopenharmony_ci ipu_ic_disable_task(&ipu_data, channel); 7428c2ecf20Sopenharmony_ci ichan->status = IPU_CHANNEL_READY; 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci /* 44.3.3.1.9 - Row Number 1 (WORD1, offset 0) */ 7468c2ecf20Sopenharmony_ci idmac_write_ipureg(&ipu_data, dma_param_addr(channel) + 7478c2ecf20Sopenharmony_ci 0x0008UL, IPU_IMA_ADDR); 7488c2ecf20Sopenharmony_ci idmac_write_ipureg(&ipu_data, phyaddr, IPU_IMA_DATA); 7498c2ecf20Sopenharmony_ci } else { 7508c2ecf20Sopenharmony_ci reg = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF1_RDY); 7518c2ecf20Sopenharmony_ci if (reg & (1UL << channel)) { 7528c2ecf20Sopenharmony_ci ipu_ic_disable_task(&ipu_data, channel); 7538c2ecf20Sopenharmony_ci ichan->status = IPU_CHANNEL_READY; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* Check if double-buffering is already enabled */ 7578c2ecf20Sopenharmony_ci reg = idmac_read_ipureg(&ipu_data, IPU_CHA_DB_MODE_SEL); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (!(reg & (1UL << channel))) 7608c2ecf20Sopenharmony_ci idmac_write_ipureg(&ipu_data, reg | (1UL << channel), 7618c2ecf20Sopenharmony_ci IPU_CHA_DB_MODE_SEL); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci /* 44.3.3.1.9 - Row Number 1 (WORD1, offset 1) */ 7648c2ecf20Sopenharmony_ci idmac_write_ipureg(&ipu_data, dma_param_addr(channel) + 7658c2ecf20Sopenharmony_ci 0x0009UL, IPU_IMA_ADDR); 7668c2ecf20Sopenharmony_ci idmac_write_ipureg(&ipu_data, phyaddr, IPU_IMA_DATA); 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu_data.lock, flags); 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci/* Called under spin_lock_irqsave(&ichan->lock) */ 7738c2ecf20Sopenharmony_cistatic int ipu_submit_buffer(struct idmac_channel *ichan, 7748c2ecf20Sopenharmony_ci struct idmac_tx_desc *desc, struct scatterlist *sg, int buf_idx) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci unsigned int chan_id = ichan->dma_chan.chan_id; 7778c2ecf20Sopenharmony_ci struct device *dev = &ichan->dma_chan.dev->device; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (async_tx_test_ack(&desc->txd)) 7808c2ecf20Sopenharmony_ci return -EINTR; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci /* 7838c2ecf20Sopenharmony_ci * On first invocation this shouldn't be necessary, the call to 7848c2ecf20Sopenharmony_ci * ipu_init_channel_buffer() above will set addresses for us, so we 7858c2ecf20Sopenharmony_ci * could make it conditional on status >= IPU_CHANNEL_ENABLED, but 7868c2ecf20Sopenharmony_ci * doing it again shouldn't hurt either. 7878c2ecf20Sopenharmony_ci */ 7888c2ecf20Sopenharmony_ci ipu_update_channel_buffer(ichan, buf_idx, sg_dma_address(sg)); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci ipu_select_buffer(chan_id, buf_idx); 7918c2ecf20Sopenharmony_ci dev_dbg(dev, "Updated sg %p on channel 0x%x buffer %d\n", 7928c2ecf20Sopenharmony_ci sg, chan_id, buf_idx); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci return 0; 7958c2ecf20Sopenharmony_ci} 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci/* Called under spin_lock_irqsave(&ichan->lock) */ 7988c2ecf20Sopenharmony_cistatic int ipu_submit_channel_buffers(struct idmac_channel *ichan, 7998c2ecf20Sopenharmony_ci struct idmac_tx_desc *desc) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci struct scatterlist *sg; 8028c2ecf20Sopenharmony_ci int i, ret = 0; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci for (i = 0, sg = desc->sg; i < 2 && sg; i++) { 8058c2ecf20Sopenharmony_ci if (!ichan->sg[i]) { 8068c2ecf20Sopenharmony_ci ichan->sg[i] = sg; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci ret = ipu_submit_buffer(ichan, desc, sg, i); 8098c2ecf20Sopenharmony_ci if (ret < 0) 8108c2ecf20Sopenharmony_ci return ret; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci sg = sg_next(sg); 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci return ret; 8178c2ecf20Sopenharmony_ci} 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cistatic dma_cookie_t idmac_tx_submit(struct dma_async_tx_descriptor *tx) 8208c2ecf20Sopenharmony_ci{ 8218c2ecf20Sopenharmony_ci struct idmac_tx_desc *desc = to_tx_desc(tx); 8228c2ecf20Sopenharmony_ci struct idmac_channel *ichan = to_idmac_chan(tx->chan); 8238c2ecf20Sopenharmony_ci struct idmac *idmac = to_idmac(tx->chan->device); 8248c2ecf20Sopenharmony_ci struct ipu *ipu = to_ipu(idmac); 8258c2ecf20Sopenharmony_ci struct device *dev = &ichan->dma_chan.dev->device; 8268c2ecf20Sopenharmony_ci dma_cookie_t cookie; 8278c2ecf20Sopenharmony_ci unsigned long flags; 8288c2ecf20Sopenharmony_ci int ret; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci /* Sanity check */ 8318c2ecf20Sopenharmony_ci if (!list_empty(&desc->list)) { 8328c2ecf20Sopenharmony_ci /* The descriptor doesn't belong to client */ 8338c2ecf20Sopenharmony_ci dev_err(dev, "Descriptor %p not prepared!\n", tx); 8348c2ecf20Sopenharmony_ci return -EBUSY; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci mutex_lock(&ichan->chan_mutex); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci async_tx_clear_ack(tx); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (ichan->status < IPU_CHANNEL_READY) { 8428c2ecf20Sopenharmony_ci struct idmac_video_param *video = &ichan->params.video; 8438c2ecf20Sopenharmony_ci /* 8448c2ecf20Sopenharmony_ci * Initial buffer assignment - the first two sg-entries from 8458c2ecf20Sopenharmony_ci * the descriptor will end up in the IDMAC buffers 8468c2ecf20Sopenharmony_ci */ 8478c2ecf20Sopenharmony_ci dma_addr_t dma_1 = sg_is_last(desc->sg) ? 0 : 8488c2ecf20Sopenharmony_ci sg_dma_address(&desc->sg[1]); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci WARN_ON(ichan->sg[0] || ichan->sg[1]); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci cookie = ipu_init_channel_buffer(ichan, 8538c2ecf20Sopenharmony_ci video->out_pixel_fmt, 8548c2ecf20Sopenharmony_ci video->out_width, 8558c2ecf20Sopenharmony_ci video->out_height, 8568c2ecf20Sopenharmony_ci video->out_stride, 8578c2ecf20Sopenharmony_ci IPU_ROTATE_NONE, 8588c2ecf20Sopenharmony_ci sg_dma_address(&desc->sg[0]), 8598c2ecf20Sopenharmony_ci dma_1); 8608c2ecf20Sopenharmony_ci if (cookie < 0) 8618c2ecf20Sopenharmony_ci goto out; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci dev_dbg(dev, "Submitting sg %p\n", &desc->sg[0]); 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci cookie = dma_cookie_assign(tx); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci /* ipu->lock can be taken under ichan->lock, but not v.v. */ 8698c2ecf20Sopenharmony_ci spin_lock_irqsave(&ichan->lock, flags); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci list_add_tail(&desc->list, &ichan->queue); 8728c2ecf20Sopenharmony_ci /* submit_buffers() atomically verifies and fills empty sg slots */ 8738c2ecf20Sopenharmony_ci ret = ipu_submit_channel_buffers(ichan, desc); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ichan->lock, flags); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci if (ret < 0) { 8788c2ecf20Sopenharmony_ci cookie = ret; 8798c2ecf20Sopenharmony_ci goto dequeue; 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci if (ichan->status < IPU_CHANNEL_ENABLED) { 8838c2ecf20Sopenharmony_ci ret = ipu_enable_channel(idmac, ichan); 8848c2ecf20Sopenharmony_ci if (ret < 0) { 8858c2ecf20Sopenharmony_ci cookie = ret; 8868c2ecf20Sopenharmony_ci goto dequeue; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci dump_idmac_reg(ipu); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_cidequeue: 8938c2ecf20Sopenharmony_ci if (cookie < 0) { 8948c2ecf20Sopenharmony_ci spin_lock_irqsave(&ichan->lock, flags); 8958c2ecf20Sopenharmony_ci list_del_init(&desc->list); 8968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ichan->lock, flags); 8978c2ecf20Sopenharmony_ci tx->cookie = cookie; 8988c2ecf20Sopenharmony_ci ichan->dma_chan.cookie = cookie; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ciout: 9028c2ecf20Sopenharmony_ci mutex_unlock(&ichan->chan_mutex); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci return cookie; 9058c2ecf20Sopenharmony_ci} 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci/* Called with ichan->chan_mutex held */ 9088c2ecf20Sopenharmony_cistatic int idmac_desc_alloc(struct idmac_channel *ichan, int n) 9098c2ecf20Sopenharmony_ci{ 9108c2ecf20Sopenharmony_ci struct idmac_tx_desc *desc = 9118c2ecf20Sopenharmony_ci vmalloc(array_size(n, sizeof(struct idmac_tx_desc))); 9128c2ecf20Sopenharmony_ci struct idmac *idmac = to_idmac(ichan->dma_chan.device); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (!desc) 9158c2ecf20Sopenharmony_ci return -ENOMEM; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci /* No interrupts, just disable the tasklet for a moment */ 9188c2ecf20Sopenharmony_ci tasklet_disable(&to_ipu(idmac)->tasklet); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci ichan->n_tx_desc = n; 9218c2ecf20Sopenharmony_ci ichan->desc = desc; 9228c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ichan->queue); 9238c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ichan->free_list); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci while (n--) { 9268c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *txd = &desc->txd; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci memset(txd, 0, sizeof(*txd)); 9298c2ecf20Sopenharmony_ci dma_async_tx_descriptor_init(txd, &ichan->dma_chan); 9308c2ecf20Sopenharmony_ci txd->tx_submit = idmac_tx_submit; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci list_add(&desc->list, &ichan->free_list); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci desc++; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci tasklet_enable(&to_ipu(idmac)->tasklet); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci return 0; 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci/** 9438c2ecf20Sopenharmony_ci * ipu_init_channel() - initialize an IPU channel. 9448c2ecf20Sopenharmony_ci * @idmac: IPU DMAC context. 9458c2ecf20Sopenharmony_ci * @ichan: pointer to the channel object. 9468c2ecf20Sopenharmony_ci * @return 0 on success or negative error code on failure. 9478c2ecf20Sopenharmony_ci */ 9488c2ecf20Sopenharmony_cistatic int ipu_init_channel(struct idmac *idmac, struct idmac_channel *ichan) 9498c2ecf20Sopenharmony_ci{ 9508c2ecf20Sopenharmony_ci union ipu_channel_param *params = &ichan->params; 9518c2ecf20Sopenharmony_ci uint32_t ipu_conf; 9528c2ecf20Sopenharmony_ci enum ipu_channel channel = ichan->dma_chan.chan_id; 9538c2ecf20Sopenharmony_ci unsigned long flags; 9548c2ecf20Sopenharmony_ci uint32_t reg; 9558c2ecf20Sopenharmony_ci struct ipu *ipu = to_ipu(idmac); 9568c2ecf20Sopenharmony_ci int ret = 0, n_desc = 0; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "init channel = %d\n", channel); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci if (channel != IDMAC_SDC_0 && channel != IDMAC_SDC_1 && 9618c2ecf20Sopenharmony_ci channel != IDMAC_IC_7) 9628c2ecf20Sopenharmony_ci return -EINVAL; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci switch (channel) { 9678c2ecf20Sopenharmony_ci case IDMAC_IC_7: 9688c2ecf20Sopenharmony_ci n_desc = 16; 9698c2ecf20Sopenharmony_ci reg = idmac_read_icreg(ipu, IC_CONF); 9708c2ecf20Sopenharmony_ci idmac_write_icreg(ipu, reg & ~IC_CONF_CSI_MEM_WR_EN, IC_CONF); 9718c2ecf20Sopenharmony_ci break; 9728c2ecf20Sopenharmony_ci case IDMAC_IC_0: 9738c2ecf20Sopenharmony_ci n_desc = 16; 9748c2ecf20Sopenharmony_ci reg = idmac_read_ipureg(ipu, IPU_FS_PROC_FLOW); 9758c2ecf20Sopenharmony_ci idmac_write_ipureg(ipu, reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW); 9768c2ecf20Sopenharmony_ci ret = ipu_ic_init_prpenc(ipu, params, true); 9778c2ecf20Sopenharmony_ci break; 9788c2ecf20Sopenharmony_ci case IDMAC_SDC_0: 9798c2ecf20Sopenharmony_ci case IDMAC_SDC_1: 9808c2ecf20Sopenharmony_ci n_desc = 4; 9818c2ecf20Sopenharmony_ci default: 9828c2ecf20Sopenharmony_ci break; 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci ipu->channel_init_mask |= 1L << channel; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci /* Enable IPU sub module */ 9888c2ecf20Sopenharmony_ci ipu_conf = idmac_read_ipureg(ipu, IPU_CONF) | 9898c2ecf20Sopenharmony_ci ipu_channel_conf_mask(channel); 9908c2ecf20Sopenharmony_ci idmac_write_ipureg(ipu, ipu_conf, IPU_CONF); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci if (n_desc && !ichan->desc) 9958c2ecf20Sopenharmony_ci ret = idmac_desc_alloc(ichan, n_desc); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci dump_idmac_reg(ipu); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci return ret; 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci/** 10038c2ecf20Sopenharmony_ci * ipu_uninit_channel() - uninitialize an IPU channel. 10048c2ecf20Sopenharmony_ci * @idmac: IPU DMAC context. 10058c2ecf20Sopenharmony_ci * @ichan: pointer to the channel object. 10068c2ecf20Sopenharmony_ci */ 10078c2ecf20Sopenharmony_cistatic void ipu_uninit_channel(struct idmac *idmac, struct idmac_channel *ichan) 10088c2ecf20Sopenharmony_ci{ 10098c2ecf20Sopenharmony_ci enum ipu_channel channel = ichan->dma_chan.chan_id; 10108c2ecf20Sopenharmony_ci unsigned long flags; 10118c2ecf20Sopenharmony_ci uint32_t reg; 10128c2ecf20Sopenharmony_ci unsigned long chan_mask = 1UL << channel; 10138c2ecf20Sopenharmony_ci uint32_t ipu_conf; 10148c2ecf20Sopenharmony_ci struct ipu *ipu = to_ipu(idmac); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci if (!(ipu->channel_init_mask & chan_mask)) { 10198c2ecf20Sopenharmony_ci dev_err(ipu->dev, "Channel already uninitialized %d\n", 10208c2ecf20Sopenharmony_ci channel); 10218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 10228c2ecf20Sopenharmony_ci return; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci /* Reset the double buffer */ 10268c2ecf20Sopenharmony_ci reg = idmac_read_ipureg(ipu, IPU_CHA_DB_MODE_SEL); 10278c2ecf20Sopenharmony_ci idmac_write_ipureg(ipu, reg & ~chan_mask, IPU_CHA_DB_MODE_SEL); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci ichan->sec_chan_en = false; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci switch (channel) { 10328c2ecf20Sopenharmony_ci case IDMAC_IC_7: 10338c2ecf20Sopenharmony_ci reg = idmac_read_icreg(ipu, IC_CONF); 10348c2ecf20Sopenharmony_ci idmac_write_icreg(ipu, reg & ~(IC_CONF_RWS_EN | IC_CONF_PRPENC_EN), 10358c2ecf20Sopenharmony_ci IC_CONF); 10368c2ecf20Sopenharmony_ci break; 10378c2ecf20Sopenharmony_ci case IDMAC_IC_0: 10388c2ecf20Sopenharmony_ci reg = idmac_read_icreg(ipu, IC_CONF); 10398c2ecf20Sopenharmony_ci idmac_write_icreg(ipu, reg & ~(IC_CONF_PRPENC_EN | IC_CONF_PRPENC_CSC1), 10408c2ecf20Sopenharmony_ci IC_CONF); 10418c2ecf20Sopenharmony_ci break; 10428c2ecf20Sopenharmony_ci case IDMAC_SDC_0: 10438c2ecf20Sopenharmony_ci case IDMAC_SDC_1: 10448c2ecf20Sopenharmony_ci default: 10458c2ecf20Sopenharmony_ci break; 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci ipu->channel_init_mask &= ~(1L << channel); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci ipu_conf = idmac_read_ipureg(ipu, IPU_CONF) & 10518c2ecf20Sopenharmony_ci ~ipu_channel_conf_mask(channel); 10528c2ecf20Sopenharmony_ci idmac_write_ipureg(ipu, ipu_conf, IPU_CONF); 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci ichan->n_tx_desc = 0; 10578c2ecf20Sopenharmony_ci vfree(ichan->desc); 10588c2ecf20Sopenharmony_ci ichan->desc = NULL; 10598c2ecf20Sopenharmony_ci} 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci/** 10628c2ecf20Sopenharmony_ci * ipu_disable_channel() - disable an IPU channel. 10638c2ecf20Sopenharmony_ci * @idmac: IPU DMAC context. 10648c2ecf20Sopenharmony_ci * @ichan: channel object pointer. 10658c2ecf20Sopenharmony_ci * @wait_for_stop: flag to set whether to wait for channel end of frame or 10668c2ecf20Sopenharmony_ci * return immediately. 10678c2ecf20Sopenharmony_ci * @return: 0 on success or negative error code on failure. 10688c2ecf20Sopenharmony_ci */ 10698c2ecf20Sopenharmony_cistatic int ipu_disable_channel(struct idmac *idmac, struct idmac_channel *ichan, 10708c2ecf20Sopenharmony_ci bool wait_for_stop) 10718c2ecf20Sopenharmony_ci{ 10728c2ecf20Sopenharmony_ci enum ipu_channel channel = ichan->dma_chan.chan_id; 10738c2ecf20Sopenharmony_ci struct ipu *ipu = to_ipu(idmac); 10748c2ecf20Sopenharmony_ci uint32_t reg; 10758c2ecf20Sopenharmony_ci unsigned long flags; 10768c2ecf20Sopenharmony_ci unsigned long chan_mask = 1UL << channel; 10778c2ecf20Sopenharmony_ci unsigned int timeout; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci if (wait_for_stop && channel != IDMAC_SDC_1 && channel != IDMAC_SDC_0) { 10808c2ecf20Sopenharmony_ci timeout = 40; 10818c2ecf20Sopenharmony_ci /* This waiting always fails. Related to spurious irq problem */ 10828c2ecf20Sopenharmony_ci while ((idmac_read_icreg(ipu, IDMAC_CHA_BUSY) & chan_mask) || 10838c2ecf20Sopenharmony_ci (ipu_channel_status(ipu, channel) == TASK_STAT_ACTIVE)) { 10848c2ecf20Sopenharmony_ci timeout--; 10858c2ecf20Sopenharmony_ci msleep(10); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci if (!timeout) { 10888c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, 10898c2ecf20Sopenharmony_ci "Warning: timeout waiting for channel %u to " 10908c2ecf20Sopenharmony_ci "stop: buf0_rdy = 0x%08X, buf1_rdy = 0x%08X, " 10918c2ecf20Sopenharmony_ci "busy = 0x%08X, tstat = 0x%08X\n", channel, 10928c2ecf20Sopenharmony_ci idmac_read_ipureg(ipu, IPU_CHA_BUF0_RDY), 10938c2ecf20Sopenharmony_ci idmac_read_ipureg(ipu, IPU_CHA_BUF1_RDY), 10948c2ecf20Sopenharmony_ci idmac_read_icreg(ipu, IDMAC_CHA_BUSY), 10958c2ecf20Sopenharmony_ci idmac_read_ipureg(ipu, IPU_TASKS_STAT)); 10968c2ecf20Sopenharmony_ci break; 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci } 10998c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "timeout = %d * 10ms\n", 40 - timeout); 11008c2ecf20Sopenharmony_ci } 11018c2ecf20Sopenharmony_ci /* SDC BG and FG must be disabled before DMA is disabled */ 11028c2ecf20Sopenharmony_ci if (wait_for_stop && (channel == IDMAC_SDC_0 || 11038c2ecf20Sopenharmony_ci channel == IDMAC_SDC_1)) { 11048c2ecf20Sopenharmony_ci for (timeout = 5; 11058c2ecf20Sopenharmony_ci timeout && !ipu_irq_status(ichan->eof_irq); timeout--) 11068c2ecf20Sopenharmony_ci msleep(5); 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci /* Disable IC task */ 11128c2ecf20Sopenharmony_ci ipu_ic_disable_task(ipu, channel); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci /* Disable DMA channel(s) */ 11158c2ecf20Sopenharmony_ci reg = idmac_read_icreg(ipu, IDMAC_CHA_EN); 11168c2ecf20Sopenharmony_ci idmac_write_icreg(ipu, reg & ~chan_mask, IDMAC_CHA_EN); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci return 0; 11218c2ecf20Sopenharmony_ci} 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_cistatic struct scatterlist *idmac_sg_next(struct idmac_channel *ichan, 11248c2ecf20Sopenharmony_ci struct idmac_tx_desc **desc, struct scatterlist *sg) 11258c2ecf20Sopenharmony_ci{ 11268c2ecf20Sopenharmony_ci struct scatterlist *sgnew = sg ? sg_next(sg) : NULL; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci if (sgnew) 11298c2ecf20Sopenharmony_ci /* next sg-element in this list */ 11308c2ecf20Sopenharmony_ci return sgnew; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci if ((*desc)->list.next == &ichan->queue) 11338c2ecf20Sopenharmony_ci /* No more descriptors on the queue */ 11348c2ecf20Sopenharmony_ci return NULL; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci /* Fetch next descriptor */ 11378c2ecf20Sopenharmony_ci *desc = list_entry((*desc)->list.next, struct idmac_tx_desc, list); 11388c2ecf20Sopenharmony_ci return (*desc)->sg; 11398c2ecf20Sopenharmony_ci} 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci/* 11428c2ecf20Sopenharmony_ci * We have several possibilities here: 11438c2ecf20Sopenharmony_ci * current BUF next BUF 11448c2ecf20Sopenharmony_ci * 11458c2ecf20Sopenharmony_ci * not last sg next not last sg 11468c2ecf20Sopenharmony_ci * not last sg next last sg 11478c2ecf20Sopenharmony_ci * last sg first sg from next descriptor 11488c2ecf20Sopenharmony_ci * last sg NULL 11498c2ecf20Sopenharmony_ci * 11508c2ecf20Sopenharmony_ci * Besides, the descriptor queue might be empty or not. We process all these 11518c2ecf20Sopenharmony_ci * cases carefully. 11528c2ecf20Sopenharmony_ci */ 11538c2ecf20Sopenharmony_cistatic irqreturn_t idmac_interrupt(int irq, void *dev_id) 11548c2ecf20Sopenharmony_ci{ 11558c2ecf20Sopenharmony_ci struct idmac_channel *ichan = dev_id; 11568c2ecf20Sopenharmony_ci struct device *dev = &ichan->dma_chan.dev->device; 11578c2ecf20Sopenharmony_ci unsigned int chan_id = ichan->dma_chan.chan_id; 11588c2ecf20Sopenharmony_ci struct scatterlist **sg, *sgnext, *sgnew = NULL; 11598c2ecf20Sopenharmony_ci /* Next transfer descriptor */ 11608c2ecf20Sopenharmony_ci struct idmac_tx_desc *desc, *descnew; 11618c2ecf20Sopenharmony_ci bool done = false; 11628c2ecf20Sopenharmony_ci u32 ready0, ready1, curbuf, err; 11638c2ecf20Sopenharmony_ci unsigned long flags; 11648c2ecf20Sopenharmony_ci struct dmaengine_desc_callback cb; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci /* IDMAC has cleared the respective BUFx_RDY bit, we manage the buffer */ 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci dev_dbg(dev, "IDMAC irq %d, buf %d\n", irq, ichan->active_buffer); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu_data.lock, flags); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci ready0 = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF0_RDY); 11738c2ecf20Sopenharmony_ci ready1 = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF1_RDY); 11748c2ecf20Sopenharmony_ci curbuf = idmac_read_ipureg(&ipu_data, IPU_CHA_CUR_BUF); 11758c2ecf20Sopenharmony_ci err = idmac_read_ipureg(&ipu_data, IPU_INT_STAT_4); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci if (err & (1 << chan_id)) { 11788c2ecf20Sopenharmony_ci idmac_write_ipureg(&ipu_data, 1 << chan_id, IPU_INT_STAT_4); 11798c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu_data.lock, flags); 11808c2ecf20Sopenharmony_ci /* 11818c2ecf20Sopenharmony_ci * Doing this 11828c2ecf20Sopenharmony_ci * ichan->sg[0] = ichan->sg[1] = NULL; 11838c2ecf20Sopenharmony_ci * you can force channel re-enable on the next tx_submit(), but 11848c2ecf20Sopenharmony_ci * this is dirty - think about descriptors with multiple 11858c2ecf20Sopenharmony_ci * sg elements. 11868c2ecf20Sopenharmony_ci */ 11878c2ecf20Sopenharmony_ci dev_warn(dev, "NFB4EOF on channel %d, ready %x, %x, cur %x\n", 11888c2ecf20Sopenharmony_ci chan_id, ready0, ready1, curbuf); 11898c2ecf20Sopenharmony_ci return IRQ_HANDLED; 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu_data.lock, flags); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* Other interrupts do not interfere with this channel */ 11948c2ecf20Sopenharmony_ci spin_lock(&ichan->lock); 11958c2ecf20Sopenharmony_ci if (unlikely((ichan->active_buffer && (ready1 >> chan_id) & 1) || 11968c2ecf20Sopenharmony_ci (!ichan->active_buffer && (ready0 >> chan_id) & 1) 11978c2ecf20Sopenharmony_ci )) { 11988c2ecf20Sopenharmony_ci spin_unlock(&ichan->lock); 11998c2ecf20Sopenharmony_ci dev_dbg(dev, 12008c2ecf20Sopenharmony_ci "IRQ with active buffer still ready on channel %x, " 12018c2ecf20Sopenharmony_ci "active %d, ready %x, %x!\n", chan_id, 12028c2ecf20Sopenharmony_ci ichan->active_buffer, ready0, ready1); 12038c2ecf20Sopenharmony_ci return IRQ_NONE; 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci if (unlikely(list_empty(&ichan->queue))) { 12078c2ecf20Sopenharmony_ci ichan->sg[ichan->active_buffer] = NULL; 12088c2ecf20Sopenharmony_ci spin_unlock(&ichan->lock); 12098c2ecf20Sopenharmony_ci dev_err(dev, 12108c2ecf20Sopenharmony_ci "IRQ without queued buffers on channel %x, active %d, " 12118c2ecf20Sopenharmony_ci "ready %x, %x!\n", chan_id, 12128c2ecf20Sopenharmony_ci ichan->active_buffer, ready0, ready1); 12138c2ecf20Sopenharmony_ci return IRQ_NONE; 12148c2ecf20Sopenharmony_ci } 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci /* 12178c2ecf20Sopenharmony_ci * active_buffer is a software flag, it shows which buffer we are 12188c2ecf20Sopenharmony_ci * currently expecting back from the hardware, IDMAC should be 12198c2ecf20Sopenharmony_ci * processing the other buffer already 12208c2ecf20Sopenharmony_ci */ 12218c2ecf20Sopenharmony_ci sg = &ichan->sg[ichan->active_buffer]; 12228c2ecf20Sopenharmony_ci sgnext = ichan->sg[!ichan->active_buffer]; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci if (!*sg) { 12258c2ecf20Sopenharmony_ci spin_unlock(&ichan->lock); 12268c2ecf20Sopenharmony_ci return IRQ_HANDLED; 12278c2ecf20Sopenharmony_ci } 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci desc = list_entry(ichan->queue.next, struct idmac_tx_desc, list); 12308c2ecf20Sopenharmony_ci descnew = desc; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci dev_dbg(dev, "IDMAC irq %d, dma %#llx, next dma %#llx, current %d, curbuf %#x\n", 12338c2ecf20Sopenharmony_ci irq, (u64)sg_dma_address(*sg), 12348c2ecf20Sopenharmony_ci sgnext ? (u64)sg_dma_address(sgnext) : 0, 12358c2ecf20Sopenharmony_ci ichan->active_buffer, curbuf); 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci /* Find the descriptor of sgnext */ 12388c2ecf20Sopenharmony_ci sgnew = idmac_sg_next(ichan, &descnew, *sg); 12398c2ecf20Sopenharmony_ci if (sgnext != sgnew) 12408c2ecf20Sopenharmony_ci dev_err(dev, "Submitted buffer %p, next buffer %p\n", sgnext, sgnew); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci /* 12438c2ecf20Sopenharmony_ci * if sgnext == NULL sg must be the last element in a scatterlist and 12448c2ecf20Sopenharmony_ci * queue must be empty 12458c2ecf20Sopenharmony_ci */ 12468c2ecf20Sopenharmony_ci if (unlikely(!sgnext)) { 12478c2ecf20Sopenharmony_ci if (!WARN_ON(sg_next(*sg))) 12488c2ecf20Sopenharmony_ci dev_dbg(dev, "Underrun on channel %x\n", chan_id); 12498c2ecf20Sopenharmony_ci ichan->sg[!ichan->active_buffer] = sgnew; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci if (unlikely(sgnew)) { 12528c2ecf20Sopenharmony_ci ipu_submit_buffer(ichan, descnew, sgnew, !ichan->active_buffer); 12538c2ecf20Sopenharmony_ci } else { 12548c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu_data.lock, flags); 12558c2ecf20Sopenharmony_ci ipu_ic_disable_task(&ipu_data, chan_id); 12568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu_data.lock, flags); 12578c2ecf20Sopenharmony_ci ichan->status = IPU_CHANNEL_READY; 12588c2ecf20Sopenharmony_ci /* Continue to check for complete descriptor */ 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci } 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci /* Calculate and submit the next sg element */ 12638c2ecf20Sopenharmony_ci sgnew = idmac_sg_next(ichan, &descnew, sgnew); 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci if (unlikely(!sg_next(*sg)) || !sgnext) { 12668c2ecf20Sopenharmony_ci /* 12678c2ecf20Sopenharmony_ci * Last element in scatterlist done, remove from the queue, 12688c2ecf20Sopenharmony_ci * _init for debugging 12698c2ecf20Sopenharmony_ci */ 12708c2ecf20Sopenharmony_ci list_del_init(&desc->list); 12718c2ecf20Sopenharmony_ci done = true; 12728c2ecf20Sopenharmony_ci } 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci *sg = sgnew; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci if (likely(sgnew) && 12778c2ecf20Sopenharmony_ci ipu_submit_buffer(ichan, descnew, sgnew, ichan->active_buffer) < 0) { 12788c2ecf20Sopenharmony_ci dmaengine_desc_get_callback(&descnew->txd, &cb); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci list_del_init(&descnew->list); 12818c2ecf20Sopenharmony_ci spin_unlock(&ichan->lock); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci dmaengine_desc_callback_invoke(&cb, NULL); 12848c2ecf20Sopenharmony_ci spin_lock(&ichan->lock); 12858c2ecf20Sopenharmony_ci } 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci /* Flip the active buffer - even if update above failed */ 12888c2ecf20Sopenharmony_ci ichan->active_buffer = !ichan->active_buffer; 12898c2ecf20Sopenharmony_ci if (done) 12908c2ecf20Sopenharmony_ci dma_cookie_complete(&desc->txd); 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci dmaengine_desc_get_callback(&desc->txd, &cb); 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci spin_unlock(&ichan->lock); 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci if (done && (desc->txd.flags & DMA_PREP_INTERRUPT)) 12978c2ecf20Sopenharmony_ci dmaengine_desc_callback_invoke(&cb, NULL); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci return IRQ_HANDLED; 13008c2ecf20Sopenharmony_ci} 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_cistatic void ipu_gc_tasklet(struct tasklet_struct *t) 13038c2ecf20Sopenharmony_ci{ 13048c2ecf20Sopenharmony_ci struct ipu *ipu = from_tasklet(ipu, t, tasklet); 13058c2ecf20Sopenharmony_ci int i; 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci for (i = 0; i < IPU_CHANNELS_NUM; i++) { 13088c2ecf20Sopenharmony_ci struct idmac_channel *ichan = ipu->channel + i; 13098c2ecf20Sopenharmony_ci struct idmac_tx_desc *desc; 13108c2ecf20Sopenharmony_ci unsigned long flags; 13118c2ecf20Sopenharmony_ci struct scatterlist *sg; 13128c2ecf20Sopenharmony_ci int j, k; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci for (j = 0; j < ichan->n_tx_desc; j++) { 13158c2ecf20Sopenharmony_ci desc = ichan->desc + j; 13168c2ecf20Sopenharmony_ci spin_lock_irqsave(&ichan->lock, flags); 13178c2ecf20Sopenharmony_ci if (async_tx_test_ack(&desc->txd)) { 13188c2ecf20Sopenharmony_ci list_move(&desc->list, &ichan->free_list); 13198c2ecf20Sopenharmony_ci for_each_sg(desc->sg, sg, desc->sg_len, k) { 13208c2ecf20Sopenharmony_ci if (ichan->sg[0] == sg) 13218c2ecf20Sopenharmony_ci ichan->sg[0] = NULL; 13228c2ecf20Sopenharmony_ci else if (ichan->sg[1] == sg) 13238c2ecf20Sopenharmony_ci ichan->sg[1] = NULL; 13248c2ecf20Sopenharmony_ci } 13258c2ecf20Sopenharmony_ci async_tx_clear_ack(&desc->txd); 13268c2ecf20Sopenharmony_ci } 13278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ichan->lock, flags); 13288c2ecf20Sopenharmony_ci } 13298c2ecf20Sopenharmony_ci } 13308c2ecf20Sopenharmony_ci} 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci/* Allocate and initialise a transfer descriptor. */ 13338c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *idmac_prep_slave_sg(struct dma_chan *chan, 13348c2ecf20Sopenharmony_ci struct scatterlist *sgl, unsigned int sg_len, 13358c2ecf20Sopenharmony_ci enum dma_transfer_direction direction, unsigned long tx_flags, 13368c2ecf20Sopenharmony_ci void *context) 13378c2ecf20Sopenharmony_ci{ 13388c2ecf20Sopenharmony_ci struct idmac_channel *ichan = to_idmac_chan(chan); 13398c2ecf20Sopenharmony_ci struct idmac_tx_desc *desc = NULL; 13408c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *txd = NULL; 13418c2ecf20Sopenharmony_ci unsigned long flags; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci /* We only can handle these three channels so far */ 13448c2ecf20Sopenharmony_ci if (chan->chan_id != IDMAC_SDC_0 && chan->chan_id != IDMAC_SDC_1 && 13458c2ecf20Sopenharmony_ci chan->chan_id != IDMAC_IC_7) 13468c2ecf20Sopenharmony_ci return NULL; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci if (!is_slave_direction(direction)) { 13498c2ecf20Sopenharmony_ci dev_err(chan->device->dev, "Invalid DMA direction %d!\n", direction); 13508c2ecf20Sopenharmony_ci return NULL; 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci mutex_lock(&ichan->chan_mutex); 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci spin_lock_irqsave(&ichan->lock, flags); 13568c2ecf20Sopenharmony_ci if (!list_empty(&ichan->free_list)) { 13578c2ecf20Sopenharmony_ci desc = list_entry(ichan->free_list.next, 13588c2ecf20Sopenharmony_ci struct idmac_tx_desc, list); 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci list_del_init(&desc->list); 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci desc->sg_len = sg_len; 13638c2ecf20Sopenharmony_ci desc->sg = sgl; 13648c2ecf20Sopenharmony_ci txd = &desc->txd; 13658c2ecf20Sopenharmony_ci txd->flags = tx_flags; 13668c2ecf20Sopenharmony_ci } 13678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ichan->lock, flags); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci mutex_unlock(&ichan->chan_mutex); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci tasklet_schedule(&to_ipu(to_idmac(chan->device))->tasklet); 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci return txd; 13748c2ecf20Sopenharmony_ci} 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci/* Re-select the current buffer and re-activate the channel */ 13778c2ecf20Sopenharmony_cistatic void idmac_issue_pending(struct dma_chan *chan) 13788c2ecf20Sopenharmony_ci{ 13798c2ecf20Sopenharmony_ci struct idmac_channel *ichan = to_idmac_chan(chan); 13808c2ecf20Sopenharmony_ci struct idmac *idmac = to_idmac(chan->device); 13818c2ecf20Sopenharmony_ci struct ipu *ipu = to_ipu(idmac); 13828c2ecf20Sopenharmony_ci unsigned long flags; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci /* This is not always needed, but doesn't hurt either */ 13858c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 13868c2ecf20Sopenharmony_ci ipu_select_buffer(chan->chan_id, ichan->active_buffer); 13878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci /* 13908c2ecf20Sopenharmony_ci * Might need to perform some parts of initialisation from 13918c2ecf20Sopenharmony_ci * ipu_enable_channel(), but not all, we do not want to reset to buffer 13928c2ecf20Sopenharmony_ci * 0, don't need to set priority again either, but re-enabling the task 13938c2ecf20Sopenharmony_ci * and the channel might be a good idea. 13948c2ecf20Sopenharmony_ci */ 13958c2ecf20Sopenharmony_ci} 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_cistatic int idmac_pause(struct dma_chan *chan) 13988c2ecf20Sopenharmony_ci{ 13998c2ecf20Sopenharmony_ci struct idmac_channel *ichan = to_idmac_chan(chan); 14008c2ecf20Sopenharmony_ci struct idmac *idmac = to_idmac(chan->device); 14018c2ecf20Sopenharmony_ci struct ipu *ipu = to_ipu(idmac); 14028c2ecf20Sopenharmony_ci struct list_head *list, *tmp; 14038c2ecf20Sopenharmony_ci unsigned long flags; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci mutex_lock(&ichan->chan_mutex); 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 14088c2ecf20Sopenharmony_ci ipu_ic_disable_task(ipu, chan->chan_id); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci /* Return all descriptors into "prepared" state */ 14118c2ecf20Sopenharmony_ci list_for_each_safe(list, tmp, &ichan->queue) 14128c2ecf20Sopenharmony_ci list_del_init(list); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci ichan->sg[0] = NULL; 14158c2ecf20Sopenharmony_ci ichan->sg[1] = NULL; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci ichan->status = IPU_CHANNEL_INITIALIZED; 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci mutex_unlock(&ichan->chan_mutex); 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci return 0; 14248c2ecf20Sopenharmony_ci} 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_cistatic int __idmac_terminate_all(struct dma_chan *chan) 14278c2ecf20Sopenharmony_ci{ 14288c2ecf20Sopenharmony_ci struct idmac_channel *ichan = to_idmac_chan(chan); 14298c2ecf20Sopenharmony_ci struct idmac *idmac = to_idmac(chan->device); 14308c2ecf20Sopenharmony_ci struct ipu *ipu = to_ipu(idmac); 14318c2ecf20Sopenharmony_ci unsigned long flags; 14328c2ecf20Sopenharmony_ci int i; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci ipu_disable_channel(idmac, ichan, 14358c2ecf20Sopenharmony_ci ichan->status >= IPU_CHANNEL_ENABLED); 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci tasklet_disable(&ipu->tasklet); 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci /* ichan->queue is modified in ISR, have to spinlock */ 14408c2ecf20Sopenharmony_ci spin_lock_irqsave(&ichan->lock, flags); 14418c2ecf20Sopenharmony_ci list_splice_init(&ichan->queue, &ichan->free_list); 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci if (ichan->desc) 14448c2ecf20Sopenharmony_ci for (i = 0; i < ichan->n_tx_desc; i++) { 14458c2ecf20Sopenharmony_ci struct idmac_tx_desc *desc = ichan->desc + i; 14468c2ecf20Sopenharmony_ci if (list_empty(&desc->list)) 14478c2ecf20Sopenharmony_ci /* Descriptor was prepared, but not submitted */ 14488c2ecf20Sopenharmony_ci list_add(&desc->list, &ichan->free_list); 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci async_tx_clear_ack(&desc->txd); 14518c2ecf20Sopenharmony_ci } 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci ichan->sg[0] = NULL; 14548c2ecf20Sopenharmony_ci ichan->sg[1] = NULL; 14558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ichan->lock, flags); 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci tasklet_enable(&ipu->tasklet); 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci ichan->status = IPU_CHANNEL_INITIALIZED; 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci return 0; 14628c2ecf20Sopenharmony_ci} 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_cistatic int idmac_terminate_all(struct dma_chan *chan) 14658c2ecf20Sopenharmony_ci{ 14668c2ecf20Sopenharmony_ci struct idmac_channel *ichan = to_idmac_chan(chan); 14678c2ecf20Sopenharmony_ci int ret; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci mutex_lock(&ichan->chan_mutex); 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci ret = __idmac_terminate_all(chan); 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci mutex_unlock(&ichan->chan_mutex); 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci return ret; 14768c2ecf20Sopenharmony_ci} 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci#ifdef DEBUG 14798c2ecf20Sopenharmony_cistatic irqreturn_t ic_sof_irq(int irq, void *dev_id) 14808c2ecf20Sopenharmony_ci{ 14818c2ecf20Sopenharmony_ci struct idmac_channel *ichan = dev_id; 14828c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Got SOF IRQ %d on Channel %d\n", 14838c2ecf20Sopenharmony_ci irq, ichan->dma_chan.chan_id); 14848c2ecf20Sopenharmony_ci disable_irq_nosync(irq); 14858c2ecf20Sopenharmony_ci return IRQ_HANDLED; 14868c2ecf20Sopenharmony_ci} 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_cistatic irqreturn_t ic_eof_irq(int irq, void *dev_id) 14898c2ecf20Sopenharmony_ci{ 14908c2ecf20Sopenharmony_ci struct idmac_channel *ichan = dev_id; 14918c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Got EOF IRQ %d on Channel %d\n", 14928c2ecf20Sopenharmony_ci irq, ichan->dma_chan.chan_id); 14938c2ecf20Sopenharmony_ci disable_irq_nosync(irq); 14948c2ecf20Sopenharmony_ci return IRQ_HANDLED; 14958c2ecf20Sopenharmony_ci} 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_cistatic int ic_sof = -EINVAL, ic_eof = -EINVAL; 14988c2ecf20Sopenharmony_ci#endif 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_cistatic int idmac_alloc_chan_resources(struct dma_chan *chan) 15018c2ecf20Sopenharmony_ci{ 15028c2ecf20Sopenharmony_ci struct idmac_channel *ichan = to_idmac_chan(chan); 15038c2ecf20Sopenharmony_ci struct idmac *idmac = to_idmac(chan->device); 15048c2ecf20Sopenharmony_ci int ret; 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci /* dmaengine.c now guarantees to only offer free channels */ 15078c2ecf20Sopenharmony_ci BUG_ON(chan->client_count > 1); 15088c2ecf20Sopenharmony_ci WARN_ON(ichan->status != IPU_CHANNEL_FREE); 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci dma_cookie_init(chan); 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci ret = ipu_irq_map(chan->chan_id); 15138c2ecf20Sopenharmony_ci if (ret < 0) 15148c2ecf20Sopenharmony_ci goto eimap; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci ichan->eof_irq = ret; 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci /* 15198c2ecf20Sopenharmony_ci * Important to first disable the channel, because maybe someone 15208c2ecf20Sopenharmony_ci * used it before us, e.g., the bootloader 15218c2ecf20Sopenharmony_ci */ 15228c2ecf20Sopenharmony_ci ipu_disable_channel(idmac, ichan, true); 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci ret = ipu_init_channel(idmac, ichan); 15258c2ecf20Sopenharmony_ci if (ret < 0) 15268c2ecf20Sopenharmony_ci goto eichan; 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci ret = request_irq(ichan->eof_irq, idmac_interrupt, 0, 15298c2ecf20Sopenharmony_ci ichan->eof_name, ichan); 15308c2ecf20Sopenharmony_ci if (ret < 0) 15318c2ecf20Sopenharmony_ci goto erirq; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci#ifdef DEBUG 15348c2ecf20Sopenharmony_ci if (chan->chan_id == IDMAC_IC_7) { 15358c2ecf20Sopenharmony_ci ic_sof = ipu_irq_map(69); 15368c2ecf20Sopenharmony_ci if (ic_sof > 0) { 15378c2ecf20Sopenharmony_ci ret = request_irq(ic_sof, ic_sof_irq, 0, "IC SOF", ichan); 15388c2ecf20Sopenharmony_ci if (ret) 15398c2ecf20Sopenharmony_ci dev_err(&chan->dev->device, "request irq failed for IC SOF"); 15408c2ecf20Sopenharmony_ci } 15418c2ecf20Sopenharmony_ci ic_eof = ipu_irq_map(70); 15428c2ecf20Sopenharmony_ci if (ic_eof > 0) { 15438c2ecf20Sopenharmony_ci ret = request_irq(ic_eof, ic_eof_irq, 0, "IC EOF", ichan); 15448c2ecf20Sopenharmony_ci if (ret) 15458c2ecf20Sopenharmony_ci dev_err(&chan->dev->device, "request irq failed for IC EOF"); 15468c2ecf20Sopenharmony_ci } 15478c2ecf20Sopenharmony_ci } 15488c2ecf20Sopenharmony_ci#endif 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci ichan->status = IPU_CHANNEL_INITIALIZED; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci dev_dbg(&chan->dev->device, "Found channel 0x%x, irq %d\n", 15538c2ecf20Sopenharmony_ci chan->chan_id, ichan->eof_irq); 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci return ret; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_cierirq: 15588c2ecf20Sopenharmony_ci ipu_uninit_channel(idmac, ichan); 15598c2ecf20Sopenharmony_cieichan: 15608c2ecf20Sopenharmony_ci ipu_irq_unmap(chan->chan_id); 15618c2ecf20Sopenharmony_cieimap: 15628c2ecf20Sopenharmony_ci return ret; 15638c2ecf20Sopenharmony_ci} 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_cistatic void idmac_free_chan_resources(struct dma_chan *chan) 15668c2ecf20Sopenharmony_ci{ 15678c2ecf20Sopenharmony_ci struct idmac_channel *ichan = to_idmac_chan(chan); 15688c2ecf20Sopenharmony_ci struct idmac *idmac = to_idmac(chan->device); 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci mutex_lock(&ichan->chan_mutex); 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci __idmac_terminate_all(chan); 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci if (ichan->status > IPU_CHANNEL_FREE) { 15758c2ecf20Sopenharmony_ci#ifdef DEBUG 15768c2ecf20Sopenharmony_ci if (chan->chan_id == IDMAC_IC_7) { 15778c2ecf20Sopenharmony_ci if (ic_sof > 0) { 15788c2ecf20Sopenharmony_ci free_irq(ic_sof, ichan); 15798c2ecf20Sopenharmony_ci ipu_irq_unmap(69); 15808c2ecf20Sopenharmony_ci ic_sof = -EINVAL; 15818c2ecf20Sopenharmony_ci } 15828c2ecf20Sopenharmony_ci if (ic_eof > 0) { 15838c2ecf20Sopenharmony_ci free_irq(ic_eof, ichan); 15848c2ecf20Sopenharmony_ci ipu_irq_unmap(70); 15858c2ecf20Sopenharmony_ci ic_eof = -EINVAL; 15868c2ecf20Sopenharmony_ci } 15878c2ecf20Sopenharmony_ci } 15888c2ecf20Sopenharmony_ci#endif 15898c2ecf20Sopenharmony_ci free_irq(ichan->eof_irq, ichan); 15908c2ecf20Sopenharmony_ci ipu_irq_unmap(chan->chan_id); 15918c2ecf20Sopenharmony_ci } 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci ichan->status = IPU_CHANNEL_FREE; 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci ipu_uninit_channel(idmac, ichan); 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci mutex_unlock(&ichan->chan_mutex); 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci tasklet_schedule(&to_ipu(idmac)->tasklet); 16008c2ecf20Sopenharmony_ci} 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_cistatic enum dma_status idmac_tx_status(struct dma_chan *chan, 16038c2ecf20Sopenharmony_ci dma_cookie_t cookie, struct dma_tx_state *txstate) 16048c2ecf20Sopenharmony_ci{ 16058c2ecf20Sopenharmony_ci return dma_cookie_status(chan, cookie, txstate); 16068c2ecf20Sopenharmony_ci} 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_cistatic int __init ipu_idmac_init(struct ipu *ipu) 16098c2ecf20Sopenharmony_ci{ 16108c2ecf20Sopenharmony_ci struct idmac *idmac = &ipu->idmac; 16118c2ecf20Sopenharmony_ci struct dma_device *dma = &idmac->dma; 16128c2ecf20Sopenharmony_ci int i; 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci dma_cap_set(DMA_SLAVE, dma->cap_mask); 16158c2ecf20Sopenharmony_ci dma_cap_set(DMA_PRIVATE, dma->cap_mask); 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci /* Compulsory common fields */ 16188c2ecf20Sopenharmony_ci dma->dev = ipu->dev; 16198c2ecf20Sopenharmony_ci dma->device_alloc_chan_resources = idmac_alloc_chan_resources; 16208c2ecf20Sopenharmony_ci dma->device_free_chan_resources = idmac_free_chan_resources; 16218c2ecf20Sopenharmony_ci dma->device_tx_status = idmac_tx_status; 16228c2ecf20Sopenharmony_ci dma->device_issue_pending = idmac_issue_pending; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci /* Compulsory for DMA_SLAVE fields */ 16258c2ecf20Sopenharmony_ci dma->device_prep_slave_sg = idmac_prep_slave_sg; 16268c2ecf20Sopenharmony_ci dma->device_pause = idmac_pause; 16278c2ecf20Sopenharmony_ci dma->device_terminate_all = idmac_terminate_all; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dma->channels); 16308c2ecf20Sopenharmony_ci for (i = 0; i < IPU_CHANNELS_NUM; i++) { 16318c2ecf20Sopenharmony_ci struct idmac_channel *ichan = ipu->channel + i; 16328c2ecf20Sopenharmony_ci struct dma_chan *dma_chan = &ichan->dma_chan; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci spin_lock_init(&ichan->lock); 16358c2ecf20Sopenharmony_ci mutex_init(&ichan->chan_mutex); 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci ichan->status = IPU_CHANNEL_FREE; 16388c2ecf20Sopenharmony_ci ichan->sec_chan_en = false; 16398c2ecf20Sopenharmony_ci snprintf(ichan->eof_name, sizeof(ichan->eof_name), "IDMAC EOF %d", i); 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci dma_chan->device = &idmac->dma; 16428c2ecf20Sopenharmony_ci dma_cookie_init(dma_chan); 16438c2ecf20Sopenharmony_ci dma_chan->chan_id = i; 16448c2ecf20Sopenharmony_ci list_add_tail(&dma_chan->device_node, &dma->channels); 16458c2ecf20Sopenharmony_ci } 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci idmac_write_icreg(ipu, 0x00000070, IDMAC_CONF); 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci return dma_async_device_register(&idmac->dma); 16508c2ecf20Sopenharmony_ci} 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_cistatic void ipu_idmac_exit(struct ipu *ipu) 16538c2ecf20Sopenharmony_ci{ 16548c2ecf20Sopenharmony_ci int i; 16558c2ecf20Sopenharmony_ci struct idmac *idmac = &ipu->idmac; 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci for (i = 0; i < IPU_CHANNELS_NUM; i++) { 16588c2ecf20Sopenharmony_ci struct idmac_channel *ichan = ipu->channel + i; 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci idmac_terminate_all(&ichan->dma_chan); 16618c2ecf20Sopenharmony_ci } 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci dma_async_device_unregister(&idmac->dma); 16648c2ecf20Sopenharmony_ci} 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci/***************************************************************************** 16678c2ecf20Sopenharmony_ci * IPU common probe / remove 16688c2ecf20Sopenharmony_ci */ 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_cistatic int __init ipu_probe(struct platform_device *pdev) 16718c2ecf20Sopenharmony_ci{ 16728c2ecf20Sopenharmony_ci struct resource *mem_ipu, *mem_ic; 16738c2ecf20Sopenharmony_ci int ret; 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci spin_lock_init(&ipu_data.lock); 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci mem_ipu = platform_get_resource(pdev, IORESOURCE_MEM, 0); 16788c2ecf20Sopenharmony_ci mem_ic = platform_get_resource(pdev, IORESOURCE_MEM, 1); 16798c2ecf20Sopenharmony_ci if (!mem_ipu || !mem_ic) 16808c2ecf20Sopenharmony_ci return -EINVAL; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci ipu_data.dev = &pdev->dev; 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, &ipu_data); 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci ret = platform_get_irq(pdev, 0); 16878c2ecf20Sopenharmony_ci if (ret < 0) 16888c2ecf20Sopenharmony_ci goto err_noirq; 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci ipu_data.irq_fn = ret; 16918c2ecf20Sopenharmony_ci ret = platform_get_irq(pdev, 1); 16928c2ecf20Sopenharmony_ci if (ret < 0) 16938c2ecf20Sopenharmony_ci goto err_noirq; 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci ipu_data.irq_err = ret; 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "fn irq %u, err irq %u\n", 16988c2ecf20Sopenharmony_ci ipu_data.irq_fn, ipu_data.irq_err); 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci /* Remap IPU common registers */ 17018c2ecf20Sopenharmony_ci ipu_data.reg_ipu = ioremap(mem_ipu->start, resource_size(mem_ipu)); 17028c2ecf20Sopenharmony_ci if (!ipu_data.reg_ipu) { 17038c2ecf20Sopenharmony_ci ret = -ENOMEM; 17048c2ecf20Sopenharmony_ci goto err_ioremap_ipu; 17058c2ecf20Sopenharmony_ci } 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci /* Remap Image Converter and Image DMA Controller registers */ 17088c2ecf20Sopenharmony_ci ipu_data.reg_ic = ioremap(mem_ic->start, resource_size(mem_ic)); 17098c2ecf20Sopenharmony_ci if (!ipu_data.reg_ic) { 17108c2ecf20Sopenharmony_ci ret = -ENOMEM; 17118c2ecf20Sopenharmony_ci goto err_ioremap_ic; 17128c2ecf20Sopenharmony_ci } 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci /* Get IPU clock */ 17158c2ecf20Sopenharmony_ci ipu_data.ipu_clk = clk_get(&pdev->dev, NULL); 17168c2ecf20Sopenharmony_ci if (IS_ERR(ipu_data.ipu_clk)) { 17178c2ecf20Sopenharmony_ci ret = PTR_ERR(ipu_data.ipu_clk); 17188c2ecf20Sopenharmony_ci goto err_clk_get; 17198c2ecf20Sopenharmony_ci } 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci /* Make sure IPU HSP clock is running */ 17228c2ecf20Sopenharmony_ci clk_prepare_enable(ipu_data.ipu_clk); 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci /* Disable all interrupts */ 17258c2ecf20Sopenharmony_ci idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_1); 17268c2ecf20Sopenharmony_ci idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_2); 17278c2ecf20Sopenharmony_ci idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_3); 17288c2ecf20Sopenharmony_ci idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_4); 17298c2ecf20Sopenharmony_ci idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_5); 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "%s @ 0x%08lx, fn irq %u, err irq %u\n", pdev->name, 17328c2ecf20Sopenharmony_ci (unsigned long)mem_ipu->start, ipu_data.irq_fn, ipu_data.irq_err); 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci ret = ipu_irq_attach_irq(&ipu_data, pdev); 17358c2ecf20Sopenharmony_ci if (ret < 0) 17368c2ecf20Sopenharmony_ci goto err_attach_irq; 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci /* Initialize DMA engine */ 17398c2ecf20Sopenharmony_ci ret = ipu_idmac_init(&ipu_data); 17408c2ecf20Sopenharmony_ci if (ret < 0) 17418c2ecf20Sopenharmony_ci goto err_idmac_init; 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci tasklet_setup(&ipu_data.tasklet, ipu_gc_tasklet); 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci ipu_data.dev = &pdev->dev; 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci dev_dbg(ipu_data.dev, "IPU initialized\n"); 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci return 0; 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_cierr_idmac_init: 17528c2ecf20Sopenharmony_cierr_attach_irq: 17538c2ecf20Sopenharmony_ci ipu_irq_detach_irq(&ipu_data, pdev); 17548c2ecf20Sopenharmony_ci clk_disable_unprepare(ipu_data.ipu_clk); 17558c2ecf20Sopenharmony_ci clk_put(ipu_data.ipu_clk); 17568c2ecf20Sopenharmony_cierr_clk_get: 17578c2ecf20Sopenharmony_ci iounmap(ipu_data.reg_ic); 17588c2ecf20Sopenharmony_cierr_ioremap_ic: 17598c2ecf20Sopenharmony_ci iounmap(ipu_data.reg_ipu); 17608c2ecf20Sopenharmony_cierr_ioremap_ipu: 17618c2ecf20Sopenharmony_cierr_noirq: 17628c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to probe IPU: %d\n", ret); 17638c2ecf20Sopenharmony_ci return ret; 17648c2ecf20Sopenharmony_ci} 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_cistatic int ipu_remove(struct platform_device *pdev) 17678c2ecf20Sopenharmony_ci{ 17688c2ecf20Sopenharmony_ci struct ipu *ipu = platform_get_drvdata(pdev); 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci ipu_idmac_exit(ipu); 17718c2ecf20Sopenharmony_ci ipu_irq_detach_irq(ipu, pdev); 17728c2ecf20Sopenharmony_ci clk_disable_unprepare(ipu->ipu_clk); 17738c2ecf20Sopenharmony_ci clk_put(ipu->ipu_clk); 17748c2ecf20Sopenharmony_ci iounmap(ipu->reg_ic); 17758c2ecf20Sopenharmony_ci iounmap(ipu->reg_ipu); 17768c2ecf20Sopenharmony_ci tasklet_kill(&ipu->tasklet); 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci return 0; 17798c2ecf20Sopenharmony_ci} 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci/* 17828c2ecf20Sopenharmony_ci * We need two MEM resources - with IPU-common and Image Converter registers, 17838c2ecf20Sopenharmony_ci * including PF_CONF and IDMAC_* registers, and two IRQs - function and error 17848c2ecf20Sopenharmony_ci */ 17858c2ecf20Sopenharmony_cistatic struct platform_driver ipu_platform_driver = { 17868c2ecf20Sopenharmony_ci .driver = { 17878c2ecf20Sopenharmony_ci .name = "ipu-core", 17888c2ecf20Sopenharmony_ci }, 17898c2ecf20Sopenharmony_ci .remove = ipu_remove, 17908c2ecf20Sopenharmony_ci}; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_cistatic int __init ipu_init(void) 17938c2ecf20Sopenharmony_ci{ 17948c2ecf20Sopenharmony_ci return platform_driver_probe(&ipu_platform_driver, ipu_probe); 17958c2ecf20Sopenharmony_ci} 17968c2ecf20Sopenharmony_cisubsys_initcall(ipu_init); 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IPU core driver"); 17998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 18008c2ecf20Sopenharmony_ciMODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>"); 18018c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:ipu-core"); 1802