18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> 48c2ecf20Sopenharmony_ci * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/module.h> 78c2ecf20Sopenharmony_ci#include <linux/export.h> 88c2ecf20Sopenharmony_ci#include <linux/types.h> 98c2ecf20Sopenharmony_ci#include <linux/reset.h> 108c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/clk.h> 178c2ecf20Sopenharmony_ci#include <linux/list.h> 188c2ecf20Sopenharmony_ci#include <linux/irq.h> 198c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 208c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 218c2ecf20Sopenharmony_ci#include <linux/of_device.h> 228c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <video/imx-ipu-v3.h> 278c2ecf20Sopenharmony_ci#include "ipu-prv.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci return readl(ipu->cm_reg + offset); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci writel(value, ipu->cm_reg + offset); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ciint ipu_get_num(struct ipu_soc *ipu) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci return ipu->id; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_get_num); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_civoid ipu_srm_dp_update(struct ipu_soc *ipu, bool sync) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci u32 val; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci val = ipu_cm_read(ipu, IPU_SRM_PRI2); 508c2ecf20Sopenharmony_ci val &= ~DP_S_SRM_MODE_MASK; 518c2ecf20Sopenharmony_ci val |= sync ? DP_S_SRM_MODE_NEXT_FRAME : 528c2ecf20Sopenharmony_ci DP_S_SRM_MODE_NOW; 538c2ecf20Sopenharmony_ci ipu_cm_write(ipu, val, IPU_SRM_PRI2); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_srm_dp_update); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cienum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci switch (drm_fourcc) { 608c2ecf20Sopenharmony_ci case DRM_FORMAT_ARGB1555: 618c2ecf20Sopenharmony_ci case DRM_FORMAT_ABGR1555: 628c2ecf20Sopenharmony_ci case DRM_FORMAT_RGBA5551: 638c2ecf20Sopenharmony_ci case DRM_FORMAT_BGRA5551: 648c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB565: 658c2ecf20Sopenharmony_ci case DRM_FORMAT_BGR565: 668c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB888: 678c2ecf20Sopenharmony_ci case DRM_FORMAT_BGR888: 688c2ecf20Sopenharmony_ci case DRM_FORMAT_ARGB4444: 698c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB8888: 708c2ecf20Sopenharmony_ci case DRM_FORMAT_XBGR8888: 718c2ecf20Sopenharmony_ci case DRM_FORMAT_RGBX8888: 728c2ecf20Sopenharmony_ci case DRM_FORMAT_BGRX8888: 738c2ecf20Sopenharmony_ci case DRM_FORMAT_ARGB8888: 748c2ecf20Sopenharmony_ci case DRM_FORMAT_ABGR8888: 758c2ecf20Sopenharmony_ci case DRM_FORMAT_RGBA8888: 768c2ecf20Sopenharmony_ci case DRM_FORMAT_BGRA8888: 778c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB565_A8: 788c2ecf20Sopenharmony_ci case DRM_FORMAT_BGR565_A8: 798c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB888_A8: 808c2ecf20Sopenharmony_ci case DRM_FORMAT_BGR888_A8: 818c2ecf20Sopenharmony_ci case DRM_FORMAT_RGBX8888_A8: 828c2ecf20Sopenharmony_ci case DRM_FORMAT_BGRX8888_A8: 838c2ecf20Sopenharmony_ci return IPUV3_COLORSPACE_RGB; 848c2ecf20Sopenharmony_ci case DRM_FORMAT_YUYV: 858c2ecf20Sopenharmony_ci case DRM_FORMAT_UYVY: 868c2ecf20Sopenharmony_ci case DRM_FORMAT_YUV420: 878c2ecf20Sopenharmony_ci case DRM_FORMAT_YVU420: 888c2ecf20Sopenharmony_ci case DRM_FORMAT_YUV422: 898c2ecf20Sopenharmony_ci case DRM_FORMAT_YVU422: 908c2ecf20Sopenharmony_ci case DRM_FORMAT_YUV444: 918c2ecf20Sopenharmony_ci case DRM_FORMAT_YVU444: 928c2ecf20Sopenharmony_ci case DRM_FORMAT_NV12: 938c2ecf20Sopenharmony_ci case DRM_FORMAT_NV21: 948c2ecf20Sopenharmony_ci case DRM_FORMAT_NV16: 958c2ecf20Sopenharmony_ci case DRM_FORMAT_NV61: 968c2ecf20Sopenharmony_ci return IPUV3_COLORSPACE_YUV; 978c2ecf20Sopenharmony_ci default: 988c2ecf20Sopenharmony_ci return IPUV3_COLORSPACE_UNKNOWN; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cienum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci switch (pixelformat) { 1068c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_YUV420: 1078c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_YVU420: 1088c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_YUV422P: 1098c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_UYVY: 1108c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 1118c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV12: 1128c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV21: 1138c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV16: 1148c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV61: 1158c2ecf20Sopenharmony_ci return IPUV3_COLORSPACE_YUV; 1168c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_RGB565: 1178c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_BGR24: 1188c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_RGB24: 1198c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_ABGR32: 1208c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_XBGR32: 1218c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_BGRA32: 1228c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_BGRX32: 1238c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_RGBA32: 1248c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_RGBX32: 1258c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_ARGB32: 1268c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_XRGB32: 1278c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_RGB32: 1288c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_BGR32: 1298c2ecf20Sopenharmony_ci return IPUV3_COLORSPACE_RGB; 1308c2ecf20Sopenharmony_ci default: 1318c2ecf20Sopenharmony_ci return IPUV3_COLORSPACE_UNKNOWN; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ciint ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees, 1378c2ecf20Sopenharmony_ci bool hflip, bool vflip) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci u32 r90, vf, hf; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci switch (degrees) { 1428c2ecf20Sopenharmony_ci case 0: 1438c2ecf20Sopenharmony_ci vf = hf = r90 = 0; 1448c2ecf20Sopenharmony_ci break; 1458c2ecf20Sopenharmony_ci case 90: 1468c2ecf20Sopenharmony_ci vf = hf = 0; 1478c2ecf20Sopenharmony_ci r90 = 1; 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci case 180: 1508c2ecf20Sopenharmony_ci vf = hf = 1; 1518c2ecf20Sopenharmony_ci r90 = 0; 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci case 270: 1548c2ecf20Sopenharmony_ci vf = hf = r90 = 1; 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci default: 1578c2ecf20Sopenharmony_ci return -EINVAL; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci hf ^= (u32)hflip; 1618c2ecf20Sopenharmony_ci vf ^= (u32)vflip; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci *mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf); 1648c2ecf20Sopenharmony_ci return 0; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ciint ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode, 1698c2ecf20Sopenharmony_ci bool hflip, bool vflip) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci u32 r90, vf, hf; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci r90 = ((u32)mode >> 2) & 0x1; 1748c2ecf20Sopenharmony_ci hf = ((u32)mode >> 1) & 0x1; 1758c2ecf20Sopenharmony_ci vf = ((u32)mode >> 0) & 0x1; 1768c2ecf20Sopenharmony_ci hf ^= (u32)hflip; 1778c2ecf20Sopenharmony_ci vf ^= (u32)vflip; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) { 1808c2ecf20Sopenharmony_ci case IPU_ROTATE_NONE: 1818c2ecf20Sopenharmony_ci *degrees = 0; 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci case IPU_ROTATE_90_RIGHT: 1848c2ecf20Sopenharmony_ci *degrees = 90; 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci case IPU_ROTATE_180: 1878c2ecf20Sopenharmony_ci *degrees = 180; 1888c2ecf20Sopenharmony_ci break; 1898c2ecf20Sopenharmony_ci case IPU_ROTATE_90_LEFT: 1908c2ecf20Sopenharmony_ci *degrees = 270; 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci default: 1938c2ecf20Sopenharmony_ci return -EINVAL; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return 0; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistruct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct ipuv3_channel *channel; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "%s %d\n", __func__, num); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (num > 63) 2078c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci mutex_lock(&ipu->channel_lock); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci list_for_each_entry(channel, &ipu->channels, list) { 2128c2ecf20Sopenharmony_ci if (channel->num == num) { 2138c2ecf20Sopenharmony_ci channel = ERR_PTR(-EBUSY); 2148c2ecf20Sopenharmony_ci goto out; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci channel = kzalloc(sizeof(*channel), GFP_KERNEL); 2198c2ecf20Sopenharmony_ci if (!channel) { 2208c2ecf20Sopenharmony_ci channel = ERR_PTR(-ENOMEM); 2218c2ecf20Sopenharmony_ci goto out; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci channel->num = num; 2258c2ecf20Sopenharmony_ci channel->ipu = ipu; 2268c2ecf20Sopenharmony_ci list_add(&channel->list, &ipu->channels); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ciout: 2298c2ecf20Sopenharmony_ci mutex_unlock(&ipu->channel_lock); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return channel; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_get); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_civoid ipu_idmac_put(struct ipuv3_channel *channel) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct ipu_soc *ipu = channel->ipu; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci mutex_lock(&ipu->channel_lock); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci list_del(&channel->list); 2448c2ecf20Sopenharmony_ci kfree(channel); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci mutex_unlock(&ipu->channel_lock); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_put); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci#define idma_mask(ch) (1 << ((ch) & 0x1f)) 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci/* 2538c2ecf20Sopenharmony_ci * This is an undocumented feature, a write one to a channel bit in 2548c2ecf20Sopenharmony_ci * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's 2558c2ecf20Sopenharmony_ci * internal current buffer pointer so that transfers start from buffer 2568c2ecf20Sopenharmony_ci * 0 on the next channel enable (that's the theory anyway, the imx6 TRM 2578c2ecf20Sopenharmony_ci * only says these are read-only registers). This operation is required 2588c2ecf20Sopenharmony_ci * for channel linking to work correctly, for instance video capture 2598c2ecf20Sopenharmony_ci * pipelines that carry out image rotations will fail after the first 2608c2ecf20Sopenharmony_ci * streaming unless this function is called for each channel before 2618c2ecf20Sopenharmony_ci * re-enabling the channels. 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_cistatic void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci struct ipu_soc *ipu = channel->ipu; 2668c2ecf20Sopenharmony_ci unsigned int chno = channel->num; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno)); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_civoid ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, 2728c2ecf20Sopenharmony_ci bool doublebuffer) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct ipu_soc *ipu = channel->ipu; 2758c2ecf20Sopenharmony_ci unsigned long flags; 2768c2ecf20Sopenharmony_ci u32 reg; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 2818c2ecf20Sopenharmony_ci if (doublebuffer) 2828c2ecf20Sopenharmony_ci reg |= idma_mask(channel->num); 2838c2ecf20Sopenharmony_ci else 2848c2ecf20Sopenharmony_ci reg &= ~idma_mask(channel->num); 2858c2ecf20Sopenharmony_ci ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num)); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci __ipu_idmac_reset_current_buffer(channel); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic const struct { 2948c2ecf20Sopenharmony_ci int chnum; 2958c2ecf20Sopenharmony_ci u32 reg; 2968c2ecf20Sopenharmony_ci int shift; 2978c2ecf20Sopenharmony_ci} idmac_lock_en_info[] = { 2988c2ecf20Sopenharmony_ci { .chnum = 5, .reg = IDMAC_CH_LOCK_EN_1, .shift = 0, }, 2998c2ecf20Sopenharmony_ci { .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift = 2, }, 3008c2ecf20Sopenharmony_ci { .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift = 4, }, 3018c2ecf20Sopenharmony_ci { .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift = 6, }, 3028c2ecf20Sopenharmony_ci { .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift = 8, }, 3038c2ecf20Sopenharmony_ci { .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, }, 3048c2ecf20Sopenharmony_ci { .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, }, 3058c2ecf20Sopenharmony_ci { .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, }, 3068c2ecf20Sopenharmony_ci { .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, }, 3078c2ecf20Sopenharmony_ci { .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, }, 3088c2ecf20Sopenharmony_ci { .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, }, 3098c2ecf20Sopenharmony_ci { .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift = 0, }, 3108c2ecf20Sopenharmony_ci { .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift = 2, }, 3118c2ecf20Sopenharmony_ci { .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift = 4, }, 3128c2ecf20Sopenharmony_ci { .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift = 6, }, 3138c2ecf20Sopenharmony_ci { .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift = 8, }, 3148c2ecf20Sopenharmony_ci { .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, }, 3158c2ecf20Sopenharmony_ci}; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ciint ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct ipu_soc *ipu = channel->ipu; 3208c2ecf20Sopenharmony_ci unsigned long flags; 3218c2ecf20Sopenharmony_ci u32 bursts, regval; 3228c2ecf20Sopenharmony_ci int i; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci switch (num_bursts) { 3258c2ecf20Sopenharmony_ci case 0: 3268c2ecf20Sopenharmony_ci case 1: 3278c2ecf20Sopenharmony_ci bursts = 0x00; /* locking disabled */ 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci case 2: 3308c2ecf20Sopenharmony_ci bursts = 0x01; 3318c2ecf20Sopenharmony_ci break; 3328c2ecf20Sopenharmony_ci case 4: 3338c2ecf20Sopenharmony_ci bursts = 0x02; 3348c2ecf20Sopenharmony_ci break; 3358c2ecf20Sopenharmony_ci case 8: 3368c2ecf20Sopenharmony_ci bursts = 0x03; 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci default: 3398c2ecf20Sopenharmony_ci return -EINVAL; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* 3438c2ecf20Sopenharmony_ci * IPUv3EX / i.MX51 has a different register layout, and on IPUv3M / 3448c2ecf20Sopenharmony_ci * i.MX53 channel arbitration locking doesn't seem to work properly. 3458c2ecf20Sopenharmony_ci * Allow enabling the lock feature on IPUv3H / i.MX6 only. 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_ci if (bursts && ipu->ipu_type != IPUV3H) 3488c2ecf20Sopenharmony_ci return -EINVAL; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) { 3518c2ecf20Sopenharmony_ci if (channel->num == idmac_lock_en_info[i].chnum) 3528c2ecf20Sopenharmony_ci break; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci if (i >= ARRAY_SIZE(idmac_lock_en_info)) 3558c2ecf20Sopenharmony_ci return -EINVAL; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg); 3608c2ecf20Sopenharmony_ci regval &= ~(0x03 << idmac_lock_en_info[i].shift); 3618c2ecf20Sopenharmony_ci regval |= (bursts << idmac_lock_en_info[i].shift); 3628c2ecf20Sopenharmony_ci ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci return 0; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_lock_enable); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ciint ipu_module_enable(struct ipu_soc *ipu, u32 mask) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci unsigned long lock_flags; 3738c2ecf20Sopenharmony_ci u32 val; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, lock_flags); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci val = ipu_cm_read(ipu, IPU_DISP_GEN); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (mask & IPU_CONF_DI0_EN) 3808c2ecf20Sopenharmony_ci val |= IPU_DI0_COUNTER_RELEASE; 3818c2ecf20Sopenharmony_ci if (mask & IPU_CONF_DI1_EN) 3828c2ecf20Sopenharmony_ci val |= IPU_DI1_COUNTER_RELEASE; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci ipu_cm_write(ipu, val, IPU_DISP_GEN); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci val = ipu_cm_read(ipu, IPU_CONF); 3878c2ecf20Sopenharmony_ci val |= mask; 3888c2ecf20Sopenharmony_ci ipu_cm_write(ipu, val, IPU_CONF); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, lock_flags); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci return 0; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_module_enable); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ciint ipu_module_disable(struct ipu_soc *ipu, u32 mask) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci unsigned long lock_flags; 3998c2ecf20Sopenharmony_ci u32 val; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, lock_flags); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci val = ipu_cm_read(ipu, IPU_CONF); 4048c2ecf20Sopenharmony_ci val &= ~mask; 4058c2ecf20Sopenharmony_ci ipu_cm_write(ipu, val, IPU_CONF); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci val = ipu_cm_read(ipu, IPU_DISP_GEN); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (mask & IPU_CONF_DI0_EN) 4108c2ecf20Sopenharmony_ci val &= ~IPU_DI0_COUNTER_RELEASE; 4118c2ecf20Sopenharmony_ci if (mask & IPU_CONF_DI1_EN) 4128c2ecf20Sopenharmony_ci val &= ~IPU_DI1_COUNTER_RELEASE; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci ipu_cm_write(ipu, val, IPU_DISP_GEN); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, lock_flags); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return 0; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_module_disable); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ciint ipu_idmac_get_current_buffer(struct ipuv3_channel *channel) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci struct ipu_soc *ipu = channel->ipu; 4258c2ecf20Sopenharmony_ci unsigned int chno = channel->num; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cibool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct ipu_soc *ipu = channel->ipu; 4348c2ecf20Sopenharmony_ci unsigned long flags; 4358c2ecf20Sopenharmony_ci u32 reg = 0; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 4388c2ecf20Sopenharmony_ci switch (buf_num) { 4398c2ecf20Sopenharmony_ci case 0: 4408c2ecf20Sopenharmony_ci reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)); 4418c2ecf20Sopenharmony_ci break; 4428c2ecf20Sopenharmony_ci case 1: 4438c2ecf20Sopenharmony_ci reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)); 4448c2ecf20Sopenharmony_ci break; 4458c2ecf20Sopenharmony_ci case 2: 4468c2ecf20Sopenharmony_ci reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num)); 4478c2ecf20Sopenharmony_ci break; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return ((reg & idma_mask(channel->num)) != 0); 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_civoid ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct ipu_soc *ipu = channel->ipu; 4588c2ecf20Sopenharmony_ci unsigned int chno = channel->num; 4598c2ecf20Sopenharmony_ci unsigned long flags; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* Mark buffer as ready. */ 4648c2ecf20Sopenharmony_ci if (buf_num == 0) 4658c2ecf20Sopenharmony_ci ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); 4668c2ecf20Sopenharmony_ci else 4678c2ecf20Sopenharmony_ci ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_select_buffer); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_civoid ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct ipu_soc *ipu = channel->ipu; 4768c2ecf20Sopenharmony_ci unsigned int chno = channel->num; 4778c2ecf20Sopenharmony_ci unsigned long flags; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */ 4828c2ecf20Sopenharmony_ci switch (buf_num) { 4838c2ecf20Sopenharmony_ci case 0: 4848c2ecf20Sopenharmony_ci ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci case 1: 4878c2ecf20Sopenharmony_ci ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); 4888c2ecf20Sopenharmony_ci break; 4898c2ecf20Sopenharmony_ci case 2: 4908c2ecf20Sopenharmony_ci ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno)); 4918c2ecf20Sopenharmony_ci break; 4928c2ecf20Sopenharmony_ci default: 4938c2ecf20Sopenharmony_ci break; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ciint ipu_idmac_enable_channel(struct ipuv3_channel *channel) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci struct ipu_soc *ipu = channel->ipu; 5048c2ecf20Sopenharmony_ci u32 val; 5058c2ecf20Sopenharmony_ci unsigned long flags; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 5108c2ecf20Sopenharmony_ci val |= idma_mask(channel->num); 5118c2ecf20Sopenharmony_ci ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci return 0; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cibool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ciint ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct ipu_soc *ipu = channel->ipu; 5288c2ecf20Sopenharmony_ci unsigned long timeout; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(ms); 5318c2ecf20Sopenharmony_ci while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) & 5328c2ecf20Sopenharmony_ci idma_mask(channel->num)) { 5338c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) 5348c2ecf20Sopenharmony_ci return -ETIMEDOUT; 5358c2ecf20Sopenharmony_ci cpu_relax(); 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci return 0; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ciint ipu_idmac_disable_channel(struct ipuv3_channel *channel) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci struct ipu_soc *ipu = channel->ipu; 5458c2ecf20Sopenharmony_ci u32 val; 5468c2ecf20Sopenharmony_ci unsigned long flags; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* Disable DMA channel(s) */ 5518c2ecf20Sopenharmony_ci val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 5528c2ecf20Sopenharmony_ci val &= ~idma_mask(channel->num); 5538c2ecf20Sopenharmony_ci ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci __ipu_idmac_reset_current_buffer(channel); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* Set channel buffers NOT to be ready */ 5588c2ecf20Sopenharmony_ci ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */ 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) & 5618c2ecf20Sopenharmony_ci idma_mask(channel->num)) { 5628c2ecf20Sopenharmony_ci ipu_cm_write(ipu, idma_mask(channel->num), 5638c2ecf20Sopenharmony_ci IPU_CHA_BUF0_RDY(channel->num)); 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) & 5678c2ecf20Sopenharmony_ci idma_mask(channel->num)) { 5688c2ecf20Sopenharmony_ci ipu_cm_write(ipu, idma_mask(channel->num), 5698c2ecf20Sopenharmony_ci IPU_CHA_BUF1_RDY(channel->num)); 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* Reset the double buffer */ 5758c2ecf20Sopenharmony_ci val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 5768c2ecf20Sopenharmony_ci val &= ~idma_mask(channel->num); 5778c2ecf20Sopenharmony_ci ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num)); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci return 0; 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_disable_channel); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci/* 5868c2ecf20Sopenharmony_ci * The imx6 rev. D TRM says that enabling the WM feature will increase 5878c2ecf20Sopenharmony_ci * a channel's priority. Refer to Table 36-8 Calculated priority value. 5888c2ecf20Sopenharmony_ci * The sub-module that is the sink or source for the channel must enable 5898c2ecf20Sopenharmony_ci * watermark signal for this to take effect (SMFC_WM for instance). 5908c2ecf20Sopenharmony_ci */ 5918c2ecf20Sopenharmony_civoid ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci struct ipu_soc *ipu = channel->ipu; 5948c2ecf20Sopenharmony_ci unsigned long flags; 5958c2ecf20Sopenharmony_ci u32 val; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num)); 6008c2ecf20Sopenharmony_ci if (enable) 6018c2ecf20Sopenharmony_ci val |= 1 << (channel->num % 32); 6028c2ecf20Sopenharmony_ci else 6038c2ecf20Sopenharmony_ci val &= ~(1 << (channel->num % 32)); 6048c2ecf20Sopenharmony_ci ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num)); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic int ipu_memory_reset(struct ipu_soc *ipu) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci unsigned long timeout; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(1000); 6178c2ecf20Sopenharmony_ci while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { 6188c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) 6198c2ecf20Sopenharmony_ci return -ETIME; 6208c2ecf20Sopenharmony_ci cpu_relax(); 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci return 0; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci/* 6278c2ecf20Sopenharmony_ci * Set the source mux for the given CSI. Selects either parallel or 6288c2ecf20Sopenharmony_ci * MIPI CSI2 sources. 6298c2ecf20Sopenharmony_ci */ 6308c2ecf20Sopenharmony_civoid ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci unsigned long flags; 6338c2ecf20Sopenharmony_ci u32 val, mask; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE : 6368c2ecf20Sopenharmony_ci IPU_CONF_CSI0_DATA_SOURCE; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci val = ipu_cm_read(ipu, IPU_CONF); 6418c2ecf20Sopenharmony_ci if (mipi_csi2) 6428c2ecf20Sopenharmony_ci val |= mask; 6438c2ecf20Sopenharmony_ci else 6448c2ecf20Sopenharmony_ci val &= ~mask; 6458c2ecf20Sopenharmony_ci ipu_cm_write(ipu, val, IPU_CONF); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_set_csi_src_mux); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci/* 6528c2ecf20Sopenharmony_ci * Set the source mux for the IC. Selects either CSI[01] or the VDI. 6538c2ecf20Sopenharmony_ci */ 6548c2ecf20Sopenharmony_civoid ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci unsigned long flags; 6578c2ecf20Sopenharmony_ci u32 val; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci val = ipu_cm_read(ipu, IPU_CONF); 6628c2ecf20Sopenharmony_ci if (vdi) 6638c2ecf20Sopenharmony_ci val |= IPU_CONF_IC_INPUT; 6648c2ecf20Sopenharmony_ci else 6658c2ecf20Sopenharmony_ci val &= ~IPU_CONF_IC_INPUT; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (csi_id == 1) 6688c2ecf20Sopenharmony_ci val |= IPU_CONF_CSI_SEL; 6698c2ecf20Sopenharmony_ci else 6708c2ecf20Sopenharmony_ci val &= ~IPU_CONF_CSI_SEL; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci ipu_cm_write(ipu, val, IPU_CONF); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_set_ic_src_mux); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci/* Frame Synchronization Unit Channel Linking */ 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_cistruct fsu_link_reg_info { 6828c2ecf20Sopenharmony_ci int chno; 6838c2ecf20Sopenharmony_ci u32 reg; 6848c2ecf20Sopenharmony_ci u32 mask; 6858c2ecf20Sopenharmony_ci u32 val; 6868c2ecf20Sopenharmony_ci}; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cistruct fsu_link_info { 6898c2ecf20Sopenharmony_ci struct fsu_link_reg_info src; 6908c2ecf20Sopenharmony_ci struct fsu_link_reg_info sink; 6918c2ecf20Sopenharmony_ci}; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic const struct fsu_link_info fsu_link_info[] = { 6948c2ecf20Sopenharmony_ci { 6958c2ecf20Sopenharmony_ci .src = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2, 6968c2ecf20Sopenharmony_ci FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC }, 6978c2ecf20Sopenharmony_ci .sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1, 6988c2ecf20Sopenharmony_ci FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC }, 6998c2ecf20Sopenharmony_ci }, { 7008c2ecf20Sopenharmony_ci .src = { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2, 7018c2ecf20Sopenharmony_ci FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF }, 7028c2ecf20Sopenharmony_ci .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1, 7038c2ecf20Sopenharmony_ci FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF }, 7048c2ecf20Sopenharmony_ci }, { 7058c2ecf20Sopenharmony_ci .src = { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2, 7068c2ecf20Sopenharmony_ci FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP }, 7078c2ecf20Sopenharmony_ci .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1, 7088c2ecf20Sopenharmony_ci FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP }, 7098c2ecf20Sopenharmony_ci }, { 7108c2ecf20Sopenharmony_ci .src = { IPUV3_CHANNEL_CSI_DIRECT, 0 }, 7118c2ecf20Sopenharmony_ci .sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1, 7128c2ecf20Sopenharmony_ci FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT }, 7138c2ecf20Sopenharmony_ci }, 7148c2ecf20Sopenharmony_ci}; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic const struct fsu_link_info *find_fsu_link_info(int src, int sink) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci int i; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) { 7218c2ecf20Sopenharmony_ci if (src == fsu_link_info[i].src.chno && 7228c2ecf20Sopenharmony_ci sink == fsu_link_info[i].sink.chno) 7238c2ecf20Sopenharmony_ci return &fsu_link_info[i]; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci return NULL; 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci/* 7308c2ecf20Sopenharmony_ci * Links a source channel to a sink channel in the FSU. 7318c2ecf20Sopenharmony_ci */ 7328c2ecf20Sopenharmony_ciint ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci const struct fsu_link_info *link; 7358c2ecf20Sopenharmony_ci u32 src_reg, sink_reg; 7368c2ecf20Sopenharmony_ci unsigned long flags; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci link = find_fsu_link_info(src_ch, sink_ch); 7398c2ecf20Sopenharmony_ci if (!link) 7408c2ecf20Sopenharmony_ci return -EINVAL; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (link->src.mask) { 7458c2ecf20Sopenharmony_ci src_reg = ipu_cm_read(ipu, link->src.reg); 7468c2ecf20Sopenharmony_ci src_reg &= ~link->src.mask; 7478c2ecf20Sopenharmony_ci src_reg |= link->src.val; 7488c2ecf20Sopenharmony_ci ipu_cm_write(ipu, src_reg, link->src.reg); 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci if (link->sink.mask) { 7528c2ecf20Sopenharmony_ci sink_reg = ipu_cm_read(ipu, link->sink.reg); 7538c2ecf20Sopenharmony_ci sink_reg &= ~link->sink.mask; 7548c2ecf20Sopenharmony_ci sink_reg |= link->sink.val; 7558c2ecf20Sopenharmony_ci ipu_cm_write(ipu, sink_reg, link->sink.reg); 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 7598c2ecf20Sopenharmony_ci return 0; 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_fsu_link); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci/* 7648c2ecf20Sopenharmony_ci * Unlinks source and sink channels in the FSU. 7658c2ecf20Sopenharmony_ci */ 7668c2ecf20Sopenharmony_ciint ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci const struct fsu_link_info *link; 7698c2ecf20Sopenharmony_ci u32 src_reg, sink_reg; 7708c2ecf20Sopenharmony_ci unsigned long flags; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci link = find_fsu_link_info(src_ch, sink_ch); 7738c2ecf20Sopenharmony_ci if (!link) 7748c2ecf20Sopenharmony_ci return -EINVAL; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipu->lock, flags); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci if (link->src.mask) { 7798c2ecf20Sopenharmony_ci src_reg = ipu_cm_read(ipu, link->src.reg); 7808c2ecf20Sopenharmony_ci src_reg &= ~link->src.mask; 7818c2ecf20Sopenharmony_ci ipu_cm_write(ipu, src_reg, link->src.reg); 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci if (link->sink.mask) { 7858c2ecf20Sopenharmony_ci sink_reg = ipu_cm_read(ipu, link->sink.reg); 7868c2ecf20Sopenharmony_ci sink_reg &= ~link->sink.mask; 7878c2ecf20Sopenharmony_ci ipu_cm_write(ipu, sink_reg, link->sink.reg); 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipu->lock, flags); 7918c2ecf20Sopenharmony_ci return 0; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_fsu_unlink); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci/* Link IDMAC channels in the FSU */ 7968c2ecf20Sopenharmony_ciint ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink) 7978c2ecf20Sopenharmony_ci{ 7988c2ecf20Sopenharmony_ci return ipu_fsu_link(src->ipu, src->num, sink->num); 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_link); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci/* Unlink IDMAC channels in the FSU */ 8038c2ecf20Sopenharmony_ciint ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink) 8048c2ecf20Sopenharmony_ci{ 8058c2ecf20Sopenharmony_ci return ipu_fsu_unlink(src->ipu, src->num, sink->num); 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_unlink); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_cistruct ipu_devtype { 8108c2ecf20Sopenharmony_ci const char *name; 8118c2ecf20Sopenharmony_ci unsigned long cm_ofs; 8128c2ecf20Sopenharmony_ci unsigned long cpmem_ofs; 8138c2ecf20Sopenharmony_ci unsigned long srm_ofs; 8148c2ecf20Sopenharmony_ci unsigned long tpm_ofs; 8158c2ecf20Sopenharmony_ci unsigned long csi0_ofs; 8168c2ecf20Sopenharmony_ci unsigned long csi1_ofs; 8178c2ecf20Sopenharmony_ci unsigned long ic_ofs; 8188c2ecf20Sopenharmony_ci unsigned long disp0_ofs; 8198c2ecf20Sopenharmony_ci unsigned long disp1_ofs; 8208c2ecf20Sopenharmony_ci unsigned long dc_tmpl_ofs; 8218c2ecf20Sopenharmony_ci unsigned long vdi_ofs; 8228c2ecf20Sopenharmony_ci enum ipuv3_type type; 8238c2ecf20Sopenharmony_ci}; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistatic struct ipu_devtype ipu_type_imx51 = { 8268c2ecf20Sopenharmony_ci .name = "IPUv3EX", 8278c2ecf20Sopenharmony_ci .cm_ofs = 0x1e000000, 8288c2ecf20Sopenharmony_ci .cpmem_ofs = 0x1f000000, 8298c2ecf20Sopenharmony_ci .srm_ofs = 0x1f040000, 8308c2ecf20Sopenharmony_ci .tpm_ofs = 0x1f060000, 8318c2ecf20Sopenharmony_ci .csi0_ofs = 0x1e030000, 8328c2ecf20Sopenharmony_ci .csi1_ofs = 0x1e038000, 8338c2ecf20Sopenharmony_ci .ic_ofs = 0x1e020000, 8348c2ecf20Sopenharmony_ci .disp0_ofs = 0x1e040000, 8358c2ecf20Sopenharmony_ci .disp1_ofs = 0x1e048000, 8368c2ecf20Sopenharmony_ci .dc_tmpl_ofs = 0x1f080000, 8378c2ecf20Sopenharmony_ci .vdi_ofs = 0x1e068000, 8388c2ecf20Sopenharmony_ci .type = IPUV3EX, 8398c2ecf20Sopenharmony_ci}; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_cistatic struct ipu_devtype ipu_type_imx53 = { 8428c2ecf20Sopenharmony_ci .name = "IPUv3M", 8438c2ecf20Sopenharmony_ci .cm_ofs = 0x06000000, 8448c2ecf20Sopenharmony_ci .cpmem_ofs = 0x07000000, 8458c2ecf20Sopenharmony_ci .srm_ofs = 0x07040000, 8468c2ecf20Sopenharmony_ci .tpm_ofs = 0x07060000, 8478c2ecf20Sopenharmony_ci .csi0_ofs = 0x06030000, 8488c2ecf20Sopenharmony_ci .csi1_ofs = 0x06038000, 8498c2ecf20Sopenharmony_ci .ic_ofs = 0x06020000, 8508c2ecf20Sopenharmony_ci .disp0_ofs = 0x06040000, 8518c2ecf20Sopenharmony_ci .disp1_ofs = 0x06048000, 8528c2ecf20Sopenharmony_ci .dc_tmpl_ofs = 0x07080000, 8538c2ecf20Sopenharmony_ci .vdi_ofs = 0x06068000, 8548c2ecf20Sopenharmony_ci .type = IPUV3M, 8558c2ecf20Sopenharmony_ci}; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_cistatic struct ipu_devtype ipu_type_imx6q = { 8588c2ecf20Sopenharmony_ci .name = "IPUv3H", 8598c2ecf20Sopenharmony_ci .cm_ofs = 0x00200000, 8608c2ecf20Sopenharmony_ci .cpmem_ofs = 0x00300000, 8618c2ecf20Sopenharmony_ci .srm_ofs = 0x00340000, 8628c2ecf20Sopenharmony_ci .tpm_ofs = 0x00360000, 8638c2ecf20Sopenharmony_ci .csi0_ofs = 0x00230000, 8648c2ecf20Sopenharmony_ci .csi1_ofs = 0x00238000, 8658c2ecf20Sopenharmony_ci .ic_ofs = 0x00220000, 8668c2ecf20Sopenharmony_ci .disp0_ofs = 0x00240000, 8678c2ecf20Sopenharmony_ci .disp1_ofs = 0x00248000, 8688c2ecf20Sopenharmony_ci .dc_tmpl_ofs = 0x00380000, 8698c2ecf20Sopenharmony_ci .vdi_ofs = 0x00268000, 8708c2ecf20Sopenharmony_ci .type = IPUV3H, 8718c2ecf20Sopenharmony_ci}; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_cistatic const struct of_device_id imx_ipu_dt_ids[] = { 8748c2ecf20Sopenharmony_ci { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, 8758c2ecf20Sopenharmony_ci { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, 8768c2ecf20Sopenharmony_ci { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, 8778c2ecf20Sopenharmony_ci { .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, }, 8788c2ecf20Sopenharmony_ci { /* sentinel */ } 8798c2ecf20Sopenharmony_ci}; 8808c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic int ipu_submodules_init(struct ipu_soc *ipu, 8838c2ecf20Sopenharmony_ci struct platform_device *pdev, unsigned long ipu_base, 8848c2ecf20Sopenharmony_ci struct clk *ipu_clk) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci char *unit; 8878c2ecf20Sopenharmony_ci int ret; 8888c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 8898c2ecf20Sopenharmony_ci const struct ipu_devtype *devtype = ipu->devtype; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs); 8928c2ecf20Sopenharmony_ci if (ret) { 8938c2ecf20Sopenharmony_ci unit = "cpmem"; 8948c2ecf20Sopenharmony_ci goto err_cpmem; 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs, 8988c2ecf20Sopenharmony_ci IPU_CONF_CSI0_EN, ipu_clk); 8998c2ecf20Sopenharmony_ci if (ret) { 9008c2ecf20Sopenharmony_ci unit = "csi0"; 9018c2ecf20Sopenharmony_ci goto err_csi_0; 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs, 9058c2ecf20Sopenharmony_ci IPU_CONF_CSI1_EN, ipu_clk); 9068c2ecf20Sopenharmony_ci if (ret) { 9078c2ecf20Sopenharmony_ci unit = "csi1"; 9088c2ecf20Sopenharmony_ci goto err_csi_1; 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci ret = ipu_ic_init(ipu, dev, 9128c2ecf20Sopenharmony_ci ipu_base + devtype->ic_ofs, 9138c2ecf20Sopenharmony_ci ipu_base + devtype->tpm_ofs); 9148c2ecf20Sopenharmony_ci if (ret) { 9158c2ecf20Sopenharmony_ci unit = "ic"; 9168c2ecf20Sopenharmony_ci goto err_ic; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs, 9208c2ecf20Sopenharmony_ci IPU_CONF_VDI_EN | IPU_CONF_ISP_EN | 9218c2ecf20Sopenharmony_ci IPU_CONF_IC_INPUT); 9228c2ecf20Sopenharmony_ci if (ret) { 9238c2ecf20Sopenharmony_ci unit = "vdi"; 9248c2ecf20Sopenharmony_ci goto err_vdi; 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci ret = ipu_image_convert_init(ipu, dev); 9288c2ecf20Sopenharmony_ci if (ret) { 9298c2ecf20Sopenharmony_ci unit = "image_convert"; 9308c2ecf20Sopenharmony_ci goto err_image_convert; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, 9348c2ecf20Sopenharmony_ci IPU_CONF_DI0_EN, ipu_clk); 9358c2ecf20Sopenharmony_ci if (ret) { 9368c2ecf20Sopenharmony_ci unit = "di0"; 9378c2ecf20Sopenharmony_ci goto err_di_0; 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs, 9418c2ecf20Sopenharmony_ci IPU_CONF_DI1_EN, ipu_clk); 9428c2ecf20Sopenharmony_ci if (ret) { 9438c2ecf20Sopenharmony_ci unit = "di1"; 9448c2ecf20Sopenharmony_ci goto err_di_1; 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs + 9488c2ecf20Sopenharmony_ci IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs); 9498c2ecf20Sopenharmony_ci if (ret) { 9508c2ecf20Sopenharmony_ci unit = "dc_template"; 9518c2ecf20Sopenharmony_ci goto err_dc; 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci ret = ipu_dmfc_init(ipu, dev, ipu_base + 9558c2ecf20Sopenharmony_ci devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk); 9568c2ecf20Sopenharmony_ci if (ret) { 9578c2ecf20Sopenharmony_ci unit = "dmfc"; 9588c2ecf20Sopenharmony_ci goto err_dmfc; 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs); 9628c2ecf20Sopenharmony_ci if (ret) { 9638c2ecf20Sopenharmony_ci unit = "dp"; 9648c2ecf20Sopenharmony_ci goto err_dp; 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci ret = ipu_smfc_init(ipu, dev, ipu_base + 9688c2ecf20Sopenharmony_ci devtype->cm_ofs + IPU_CM_SMFC_REG_OFS); 9698c2ecf20Sopenharmony_ci if (ret) { 9708c2ecf20Sopenharmony_ci unit = "smfc"; 9718c2ecf20Sopenharmony_ci goto err_smfc; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci return 0; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_cierr_smfc: 9778c2ecf20Sopenharmony_ci ipu_dp_exit(ipu); 9788c2ecf20Sopenharmony_cierr_dp: 9798c2ecf20Sopenharmony_ci ipu_dmfc_exit(ipu); 9808c2ecf20Sopenharmony_cierr_dmfc: 9818c2ecf20Sopenharmony_ci ipu_dc_exit(ipu); 9828c2ecf20Sopenharmony_cierr_dc: 9838c2ecf20Sopenharmony_ci ipu_di_exit(ipu, 1); 9848c2ecf20Sopenharmony_cierr_di_1: 9858c2ecf20Sopenharmony_ci ipu_di_exit(ipu, 0); 9868c2ecf20Sopenharmony_cierr_di_0: 9878c2ecf20Sopenharmony_ci ipu_image_convert_exit(ipu); 9888c2ecf20Sopenharmony_cierr_image_convert: 9898c2ecf20Sopenharmony_ci ipu_vdi_exit(ipu); 9908c2ecf20Sopenharmony_cierr_vdi: 9918c2ecf20Sopenharmony_ci ipu_ic_exit(ipu); 9928c2ecf20Sopenharmony_cierr_ic: 9938c2ecf20Sopenharmony_ci ipu_csi_exit(ipu, 1); 9948c2ecf20Sopenharmony_cierr_csi_1: 9958c2ecf20Sopenharmony_ci ipu_csi_exit(ipu, 0); 9968c2ecf20Sopenharmony_cierr_csi_0: 9978c2ecf20Sopenharmony_ci ipu_cpmem_exit(ipu); 9988c2ecf20Sopenharmony_cierr_cpmem: 9998c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); 10008c2ecf20Sopenharmony_ci return ret; 10018c2ecf20Sopenharmony_ci} 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_cistatic void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci unsigned long status; 10068c2ecf20Sopenharmony_ci int i, bit, irq; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci for (i = 0; i < num_regs; i++) { 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i])); 10118c2ecf20Sopenharmony_ci status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i])); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci for_each_set_bit(bit, &status, 32) { 10148c2ecf20Sopenharmony_ci irq = irq_linear_revmap(ipu->domain, 10158c2ecf20Sopenharmony_ci regs[i] * 32 + bit); 10168c2ecf20Sopenharmony_ci if (irq) 10178c2ecf20Sopenharmony_ci generic_handle_irq(irq); 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci} 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_cistatic void ipu_irq_handler(struct irq_desc *desc) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 10258c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 10268c2ecf20Sopenharmony_ci static const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14}; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci chained_irq_enter(chip, desc); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci chained_irq_exit(chip, desc); 10338c2ecf20Sopenharmony_ci} 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_cistatic void ipu_err_irq_handler(struct irq_desc *desc) 10368c2ecf20Sopenharmony_ci{ 10378c2ecf20Sopenharmony_ci struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 10388c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 10398c2ecf20Sopenharmony_ci static const int int_reg[] = { 4, 5, 8, 9}; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci chained_irq_enter(chip, desc); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci chained_irq_exit(chip, desc); 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ciint ipu_map_irq(struct ipu_soc *ipu, int irq) 10498c2ecf20Sopenharmony_ci{ 10508c2ecf20Sopenharmony_ci int virq; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci virq = irq_linear_revmap(ipu->domain, irq); 10538c2ecf20Sopenharmony_ci if (!virq) 10548c2ecf20Sopenharmony_ci virq = irq_create_mapping(ipu->domain, irq); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci return virq; 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_map_irq); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ciint ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, 10618c2ecf20Sopenharmony_ci enum ipu_channel_irq irq_type) 10628c2ecf20Sopenharmony_ci{ 10638c2ecf20Sopenharmony_ci return ipu_map_irq(ipu, irq_type + channel->num); 10648c2ecf20Sopenharmony_ci} 10658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_cistatic void ipu_submodules_exit(struct ipu_soc *ipu) 10688c2ecf20Sopenharmony_ci{ 10698c2ecf20Sopenharmony_ci ipu_smfc_exit(ipu); 10708c2ecf20Sopenharmony_ci ipu_dp_exit(ipu); 10718c2ecf20Sopenharmony_ci ipu_dmfc_exit(ipu); 10728c2ecf20Sopenharmony_ci ipu_dc_exit(ipu); 10738c2ecf20Sopenharmony_ci ipu_di_exit(ipu, 1); 10748c2ecf20Sopenharmony_ci ipu_di_exit(ipu, 0); 10758c2ecf20Sopenharmony_ci ipu_image_convert_exit(ipu); 10768c2ecf20Sopenharmony_ci ipu_vdi_exit(ipu); 10778c2ecf20Sopenharmony_ci ipu_ic_exit(ipu); 10788c2ecf20Sopenharmony_ci ipu_csi_exit(ipu, 1); 10798c2ecf20Sopenharmony_ci ipu_csi_exit(ipu, 0); 10808c2ecf20Sopenharmony_ci ipu_cpmem_exit(ipu); 10818c2ecf20Sopenharmony_ci} 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_cistatic int platform_remove_devices_fn(struct device *dev, void *unused) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci platform_device_unregister(pdev); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci return 0; 10908c2ecf20Sopenharmony_ci} 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_cistatic void platform_device_unregister_children(struct platform_device *pdev) 10938c2ecf20Sopenharmony_ci{ 10948c2ecf20Sopenharmony_ci device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn); 10958c2ecf20Sopenharmony_ci} 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_cistruct ipu_platform_reg { 10988c2ecf20Sopenharmony_ci struct ipu_client_platformdata pdata; 10998c2ecf20Sopenharmony_ci const char *name; 11008c2ecf20Sopenharmony_ci}; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci/* These must be in the order of the corresponding device tree port nodes */ 11038c2ecf20Sopenharmony_cistatic struct ipu_platform_reg client_reg[] = { 11048c2ecf20Sopenharmony_ci { 11058c2ecf20Sopenharmony_ci .pdata = { 11068c2ecf20Sopenharmony_ci .csi = 0, 11078c2ecf20Sopenharmony_ci .dma[0] = IPUV3_CHANNEL_CSI0, 11088c2ecf20Sopenharmony_ci .dma[1] = -EINVAL, 11098c2ecf20Sopenharmony_ci }, 11108c2ecf20Sopenharmony_ci .name = "imx-ipuv3-csi", 11118c2ecf20Sopenharmony_ci }, { 11128c2ecf20Sopenharmony_ci .pdata = { 11138c2ecf20Sopenharmony_ci .csi = 1, 11148c2ecf20Sopenharmony_ci .dma[0] = IPUV3_CHANNEL_CSI1, 11158c2ecf20Sopenharmony_ci .dma[1] = -EINVAL, 11168c2ecf20Sopenharmony_ci }, 11178c2ecf20Sopenharmony_ci .name = "imx-ipuv3-csi", 11188c2ecf20Sopenharmony_ci }, { 11198c2ecf20Sopenharmony_ci .pdata = { 11208c2ecf20Sopenharmony_ci .di = 0, 11218c2ecf20Sopenharmony_ci .dc = 5, 11228c2ecf20Sopenharmony_ci .dp = IPU_DP_FLOW_SYNC_BG, 11238c2ecf20Sopenharmony_ci .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC, 11248c2ecf20Sopenharmony_ci .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC, 11258c2ecf20Sopenharmony_ci }, 11268c2ecf20Sopenharmony_ci .name = "imx-ipuv3-crtc", 11278c2ecf20Sopenharmony_ci }, { 11288c2ecf20Sopenharmony_ci .pdata = { 11298c2ecf20Sopenharmony_ci .di = 1, 11308c2ecf20Sopenharmony_ci .dc = 1, 11318c2ecf20Sopenharmony_ci .dp = -EINVAL, 11328c2ecf20Sopenharmony_ci .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC, 11338c2ecf20Sopenharmony_ci .dma[1] = -EINVAL, 11348c2ecf20Sopenharmony_ci }, 11358c2ecf20Sopenharmony_ci .name = "imx-ipuv3-crtc", 11368c2ecf20Sopenharmony_ci }, 11378c2ecf20Sopenharmony_ci}; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ipu_client_id_mutex); 11408c2ecf20Sopenharmony_cistatic int ipu_client_id; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_cistatic int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) 11438c2ecf20Sopenharmony_ci{ 11448c2ecf20Sopenharmony_ci struct device *dev = ipu->dev; 11458c2ecf20Sopenharmony_ci unsigned i; 11468c2ecf20Sopenharmony_ci int id, ret; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci mutex_lock(&ipu_client_id_mutex); 11498c2ecf20Sopenharmony_ci id = ipu_client_id; 11508c2ecf20Sopenharmony_ci ipu_client_id += ARRAY_SIZE(client_reg); 11518c2ecf20Sopenharmony_ci mutex_unlock(&ipu_client_id_mutex); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(client_reg); i++) { 11548c2ecf20Sopenharmony_ci struct ipu_platform_reg *reg = &client_reg[i]; 11558c2ecf20Sopenharmony_ci struct platform_device *pdev; 11568c2ecf20Sopenharmony_ci struct device_node *of_node; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci /* Associate subdevice with the corresponding port node */ 11598c2ecf20Sopenharmony_ci of_node = of_graph_get_port_by_id(dev->of_node, i); 11608c2ecf20Sopenharmony_ci if (!of_node) { 11618c2ecf20Sopenharmony_ci dev_info(dev, 11628c2ecf20Sopenharmony_ci "no port@%d node in %pOF, not using %s%d\n", 11638c2ecf20Sopenharmony_ci i, dev->of_node, 11648c2ecf20Sopenharmony_ci (i / 2) ? "DI" : "CSI", i % 2); 11658c2ecf20Sopenharmony_ci continue; 11668c2ecf20Sopenharmony_ci } 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci pdev = platform_device_alloc(reg->name, id++); 11698c2ecf20Sopenharmony_ci if (!pdev) { 11708c2ecf20Sopenharmony_ci ret = -ENOMEM; 11718c2ecf20Sopenharmony_ci of_node_put(of_node); 11728c2ecf20Sopenharmony_ci goto err_register; 11738c2ecf20Sopenharmony_ci } 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci pdev->dev.parent = dev; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci reg->pdata.of_node = of_node; 11788c2ecf20Sopenharmony_ci ret = platform_device_add_data(pdev, ®->pdata, 11798c2ecf20Sopenharmony_ci sizeof(reg->pdata)); 11808c2ecf20Sopenharmony_ci if (!ret) 11818c2ecf20Sopenharmony_ci ret = platform_device_add(pdev); 11828c2ecf20Sopenharmony_ci if (ret) { 11838c2ecf20Sopenharmony_ci platform_device_put(pdev); 11848c2ecf20Sopenharmony_ci goto err_register; 11858c2ecf20Sopenharmony_ci } 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci return 0; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_cierr_register: 11918c2ecf20Sopenharmony_ci platform_device_unregister_children(to_platform_device(dev)); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci return ret; 11948c2ecf20Sopenharmony_ci} 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_cistatic int ipu_irq_init(struct ipu_soc *ipu) 11988c2ecf20Sopenharmony_ci{ 11998c2ecf20Sopenharmony_ci struct irq_chip_generic *gc; 12008c2ecf20Sopenharmony_ci struct irq_chip_type *ct; 12018c2ecf20Sopenharmony_ci unsigned long unused[IPU_NUM_IRQS / 32] = { 12028c2ecf20Sopenharmony_ci 0x400100d0, 0xffe000fd, 12038c2ecf20Sopenharmony_ci 0x400100d0, 0xffe000fd, 12048c2ecf20Sopenharmony_ci 0x400100d0, 0xffe000fd, 12058c2ecf20Sopenharmony_ci 0x4077ffff, 0xffe7e1fd, 12068c2ecf20Sopenharmony_ci 0x23fffffe, 0x8880fff0, 12078c2ecf20Sopenharmony_ci 0xf98fe7d0, 0xfff81fff, 12088c2ecf20Sopenharmony_ci 0x400100d0, 0xffe000fd, 12098c2ecf20Sopenharmony_ci 0x00000000, 12108c2ecf20Sopenharmony_ci }; 12118c2ecf20Sopenharmony_ci int ret, i; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS, 12148c2ecf20Sopenharmony_ci &irq_generic_chip_ops, ipu); 12158c2ecf20Sopenharmony_ci if (!ipu->domain) { 12168c2ecf20Sopenharmony_ci dev_err(ipu->dev, "failed to add irq domain\n"); 12178c2ecf20Sopenharmony_ci return -ENODEV; 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", 12218c2ecf20Sopenharmony_ci handle_level_irq, 0, 0, 0); 12228c2ecf20Sopenharmony_ci if (ret < 0) { 12238c2ecf20Sopenharmony_ci dev_err(ipu->dev, "failed to alloc generic irq chips\n"); 12248c2ecf20Sopenharmony_ci irq_domain_remove(ipu->domain); 12258c2ecf20Sopenharmony_ci return ret; 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci /* Mask and clear all interrupts */ 12298c2ecf20Sopenharmony_ci for (i = 0; i < IPU_NUM_IRQS; i += 32) { 12308c2ecf20Sopenharmony_ci ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32)); 12318c2ecf20Sopenharmony_ci ipu_cm_write(ipu, ~unused[i / 32], IPU_INT_STAT(i / 32)); 12328c2ecf20Sopenharmony_ci } 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci for (i = 0; i < IPU_NUM_IRQS; i += 32) { 12358c2ecf20Sopenharmony_ci gc = irq_get_domain_generic_chip(ipu->domain, i); 12368c2ecf20Sopenharmony_ci gc->reg_base = ipu->cm_reg; 12378c2ecf20Sopenharmony_ci gc->unused = unused[i / 32]; 12388c2ecf20Sopenharmony_ci ct = gc->chip_types; 12398c2ecf20Sopenharmony_ci ct->chip.irq_ack = irq_gc_ack_set_bit; 12408c2ecf20Sopenharmony_ci ct->chip.irq_mask = irq_gc_mask_clr_bit; 12418c2ecf20Sopenharmony_ci ct->chip.irq_unmask = irq_gc_mask_set_bit; 12428c2ecf20Sopenharmony_ci ct->regs.ack = IPU_INT_STAT(i / 32); 12438c2ecf20Sopenharmony_ci ct->regs.mask = IPU_INT_CTRL(i / 32); 12448c2ecf20Sopenharmony_ci } 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu); 12478c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler, 12488c2ecf20Sopenharmony_ci ipu); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci return 0; 12518c2ecf20Sopenharmony_ci} 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_cistatic void ipu_irq_exit(struct ipu_soc *ipu) 12548c2ecf20Sopenharmony_ci{ 12558c2ecf20Sopenharmony_ci int i, irq; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL); 12588c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL); 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci /* TODO: remove irq_domain_generic_chips */ 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci for (i = 0; i < IPU_NUM_IRQS; i++) { 12638c2ecf20Sopenharmony_ci irq = irq_linear_revmap(ipu->domain, i); 12648c2ecf20Sopenharmony_ci if (irq) 12658c2ecf20Sopenharmony_ci irq_dispose_mapping(irq); 12668c2ecf20Sopenharmony_ci } 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci irq_domain_remove(ipu->domain); 12698c2ecf20Sopenharmony_ci} 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_civoid ipu_dump(struct ipu_soc *ipu) 12728c2ecf20Sopenharmony_ci{ 12738c2ecf20Sopenharmony_ci int i; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n", 12768c2ecf20Sopenharmony_ci ipu_cm_read(ipu, IPU_CONF)); 12778c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n", 12788c2ecf20Sopenharmony_ci ipu_idmac_read(ipu, IDMAC_CONF)); 12798c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n", 12808c2ecf20Sopenharmony_ci ipu_idmac_read(ipu, IDMAC_CHA_EN(0))); 12818c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n", 12828c2ecf20Sopenharmony_ci ipu_idmac_read(ipu, IDMAC_CHA_EN(32))); 12838c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n", 12848c2ecf20Sopenharmony_ci ipu_idmac_read(ipu, IDMAC_CHA_PRI(0))); 12858c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n", 12868c2ecf20Sopenharmony_ci ipu_idmac_read(ipu, IDMAC_CHA_PRI(32))); 12878c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n", 12888c2ecf20Sopenharmony_ci ipu_idmac_read(ipu, IDMAC_BAND_EN(0))); 12898c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n", 12908c2ecf20Sopenharmony_ci ipu_idmac_read(ipu, IDMAC_BAND_EN(32))); 12918c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", 12928c2ecf20Sopenharmony_ci ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0))); 12938c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", 12948c2ecf20Sopenharmony_ci ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32))); 12958c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n", 12968c2ecf20Sopenharmony_ci ipu_cm_read(ipu, IPU_FS_PROC_FLOW1)); 12978c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n", 12988c2ecf20Sopenharmony_ci ipu_cm_read(ipu, IPU_FS_PROC_FLOW2)); 12998c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n", 13008c2ecf20Sopenharmony_ci ipu_cm_read(ipu, IPU_FS_PROC_FLOW3)); 13018c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n", 13028c2ecf20Sopenharmony_ci ipu_cm_read(ipu, IPU_FS_DISP_FLOW1)); 13038c2ecf20Sopenharmony_ci for (i = 0; i < 15; i++) 13048c2ecf20Sopenharmony_ci dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i, 13058c2ecf20Sopenharmony_ci ipu_cm_read(ipu, IPU_INT_CTRL(i))); 13068c2ecf20Sopenharmony_ci} 13078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_dump); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_cistatic int ipu_probe(struct platform_device *pdev) 13108c2ecf20Sopenharmony_ci{ 13118c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 13128c2ecf20Sopenharmony_ci struct ipu_soc *ipu; 13138c2ecf20Sopenharmony_ci struct resource *res; 13148c2ecf20Sopenharmony_ci unsigned long ipu_base; 13158c2ecf20Sopenharmony_ci int ret, irq_sync, irq_err; 13168c2ecf20Sopenharmony_ci const struct ipu_devtype *devtype; 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci devtype = of_device_get_match_data(&pdev->dev); 13198c2ecf20Sopenharmony_ci if (!devtype) 13208c2ecf20Sopenharmony_ci return -EINVAL; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci irq_sync = platform_get_irq(pdev, 0); 13238c2ecf20Sopenharmony_ci irq_err = platform_get_irq(pdev, 1); 13248c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n", 13278c2ecf20Sopenharmony_ci irq_sync, irq_err); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci if (!res || irq_sync < 0 || irq_err < 0) 13308c2ecf20Sopenharmony_ci return -ENODEV; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci ipu_base = res->start; 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL); 13358c2ecf20Sopenharmony_ci if (!ipu) 13368c2ecf20Sopenharmony_ci return -ENODEV; 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci ipu->id = of_alias_get_id(np, "ipu"); 13398c2ecf20Sopenharmony_ci if (ipu->id < 0) 13408c2ecf20Sopenharmony_ci ipu->id = 0; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "fsl,imx6qp-ipu") && 13438c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_DRM)) { 13448c2ecf20Sopenharmony_ci ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev, 13458c2ecf20Sopenharmony_ci "fsl,prg", ipu->id); 13468c2ecf20Sopenharmony_ci if (!ipu->prg_priv) 13478c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 13488c2ecf20Sopenharmony_ci } 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci ipu->devtype = devtype; 13518c2ecf20Sopenharmony_ci ipu->ipu_type = devtype->type; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci spin_lock_init(&ipu->lock); 13548c2ecf20Sopenharmony_ci mutex_init(&ipu->channel_lock); 13558c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ipu->channels); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n", 13588c2ecf20Sopenharmony_ci ipu_base + devtype->cm_ofs); 13598c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "idmac: 0x%08lx\n", 13608c2ecf20Sopenharmony_ci ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS); 13618c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n", 13628c2ecf20Sopenharmony_ci ipu_base + devtype->cpmem_ofs); 13638c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "csi0: 0x%08lx\n", 13648c2ecf20Sopenharmony_ci ipu_base + devtype->csi0_ofs); 13658c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "csi1: 0x%08lx\n", 13668c2ecf20Sopenharmony_ci ipu_base + devtype->csi1_ofs); 13678c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "ic: 0x%08lx\n", 13688c2ecf20Sopenharmony_ci ipu_base + devtype->ic_ofs); 13698c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "disp0: 0x%08lx\n", 13708c2ecf20Sopenharmony_ci ipu_base + devtype->disp0_ofs); 13718c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "disp1: 0x%08lx\n", 13728c2ecf20Sopenharmony_ci ipu_base + devtype->disp1_ofs); 13738c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "srm: 0x%08lx\n", 13748c2ecf20Sopenharmony_ci ipu_base + devtype->srm_ofs); 13758c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "tpm: 0x%08lx\n", 13768c2ecf20Sopenharmony_ci ipu_base + devtype->tpm_ofs); 13778c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "dc: 0x%08lx\n", 13788c2ecf20Sopenharmony_ci ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS); 13798c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "ic: 0x%08lx\n", 13808c2ecf20Sopenharmony_ci ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS); 13818c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n", 13828c2ecf20Sopenharmony_ci ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS); 13838c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "vdi: 0x%08lx\n", 13848c2ecf20Sopenharmony_ci ipu_base + devtype->vdi_ofs); 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci ipu->cm_reg = devm_ioremap(&pdev->dev, 13878c2ecf20Sopenharmony_ci ipu_base + devtype->cm_ofs, PAGE_SIZE); 13888c2ecf20Sopenharmony_ci ipu->idmac_reg = devm_ioremap(&pdev->dev, 13898c2ecf20Sopenharmony_ci ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, 13908c2ecf20Sopenharmony_ci PAGE_SIZE); 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci if (!ipu->cm_reg || !ipu->idmac_reg) 13938c2ecf20Sopenharmony_ci return -ENOMEM; 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci ipu->clk = devm_clk_get(&pdev->dev, "bus"); 13968c2ecf20Sopenharmony_ci if (IS_ERR(ipu->clk)) { 13978c2ecf20Sopenharmony_ci ret = PTR_ERR(ipu->clk); 13988c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "clk_get failed with %d", ret); 13998c2ecf20Sopenharmony_ci return ret; 14008c2ecf20Sopenharmony_ci } 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ipu); 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci ret = clk_prepare_enable(ipu->clk); 14058c2ecf20Sopenharmony_ci if (ret) { 14068c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); 14078c2ecf20Sopenharmony_ci return ret; 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci ipu->dev = &pdev->dev; 14118c2ecf20Sopenharmony_ci ipu->irq_sync = irq_sync; 14128c2ecf20Sopenharmony_ci ipu->irq_err = irq_err; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci ret = device_reset(&pdev->dev); 14158c2ecf20Sopenharmony_ci if (ret) { 14168c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to reset: %d\n", ret); 14178c2ecf20Sopenharmony_ci goto out_failed_reset; 14188c2ecf20Sopenharmony_ci } 14198c2ecf20Sopenharmony_ci ret = ipu_memory_reset(ipu); 14208c2ecf20Sopenharmony_ci if (ret) 14218c2ecf20Sopenharmony_ci goto out_failed_reset; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci ret = ipu_irq_init(ipu); 14248c2ecf20Sopenharmony_ci if (ret) 14258c2ecf20Sopenharmony_ci goto out_failed_irq; 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci /* Set MCU_T to divide MCU access window into 2 */ 14288c2ecf20Sopenharmony_ci ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), 14298c2ecf20Sopenharmony_ci IPU_DISP_GEN); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk); 14328c2ecf20Sopenharmony_ci if (ret) 14338c2ecf20Sopenharmony_ci goto failed_submodules_init; 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci ret = ipu_add_client_devices(ipu, ipu_base); 14368c2ecf20Sopenharmony_ci if (ret) { 14378c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "adding client devices failed with %d\n", 14388c2ecf20Sopenharmony_ci ret); 14398c2ecf20Sopenharmony_ci goto failed_add_clients; 14408c2ecf20Sopenharmony_ci } 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "%s probed\n", devtype->name); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci return 0; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_cifailed_add_clients: 14478c2ecf20Sopenharmony_ci ipu_submodules_exit(ipu); 14488c2ecf20Sopenharmony_cifailed_submodules_init: 14498c2ecf20Sopenharmony_ci ipu_irq_exit(ipu); 14508c2ecf20Sopenharmony_ciout_failed_irq: 14518c2ecf20Sopenharmony_ciout_failed_reset: 14528c2ecf20Sopenharmony_ci clk_disable_unprepare(ipu->clk); 14538c2ecf20Sopenharmony_ci return ret; 14548c2ecf20Sopenharmony_ci} 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_cistatic int ipu_remove(struct platform_device *pdev) 14578c2ecf20Sopenharmony_ci{ 14588c2ecf20Sopenharmony_ci struct ipu_soc *ipu = platform_get_drvdata(pdev); 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci platform_device_unregister_children(pdev); 14618c2ecf20Sopenharmony_ci ipu_submodules_exit(ipu); 14628c2ecf20Sopenharmony_ci ipu_irq_exit(ipu); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci clk_disable_unprepare(ipu->clk); 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci return 0; 14678c2ecf20Sopenharmony_ci} 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_cistatic struct platform_driver imx_ipu_driver = { 14708c2ecf20Sopenharmony_ci .driver = { 14718c2ecf20Sopenharmony_ci .name = "imx-ipuv3", 14728c2ecf20Sopenharmony_ci .of_match_table = imx_ipu_dt_ids, 14738c2ecf20Sopenharmony_ci }, 14748c2ecf20Sopenharmony_ci .probe = ipu_probe, 14758c2ecf20Sopenharmony_ci .remove = ipu_remove, 14768c2ecf20Sopenharmony_ci}; 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_cistatic struct platform_driver * const drivers[] = { 14798c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_DRM) 14808c2ecf20Sopenharmony_ci &ipu_pre_drv, 14818c2ecf20Sopenharmony_ci &ipu_prg_drv, 14828c2ecf20Sopenharmony_ci#endif 14838c2ecf20Sopenharmony_ci &imx_ipu_driver, 14848c2ecf20Sopenharmony_ci}; 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_cistatic int __init imx_ipu_init(void) 14878c2ecf20Sopenharmony_ci{ 14888c2ecf20Sopenharmony_ci return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); 14898c2ecf20Sopenharmony_ci} 14908c2ecf20Sopenharmony_cimodule_init(imx_ipu_init); 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_cistatic void __exit imx_ipu_exit(void) 14938c2ecf20Sopenharmony_ci{ 14948c2ecf20Sopenharmony_ci platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); 14958c2ecf20Sopenharmony_ci} 14968c2ecf20Sopenharmony_cimodule_exit(imx_ipu_exit); 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:imx-ipuv3"); 14998c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("i.MX IPU v3 driver"); 15008c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 15018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1502