1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 239b9004dSPhilipp Zabel /* 339b9004dSPhilipp Zabel * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> 439b9004dSPhilipp Zabel * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. 539b9004dSPhilipp Zabel */ 639b9004dSPhilipp Zabel #include <linux/module.h> 739b9004dSPhilipp Zabel #include <linux/export.h> 839b9004dSPhilipp Zabel #include <linux/types.h> 939b9004dSPhilipp Zabel #include <linux/reset.h> 1039b9004dSPhilipp Zabel #include <linux/platform_device.h> 1139b9004dSPhilipp Zabel #include <linux/err.h> 1239b9004dSPhilipp Zabel #include <linux/spinlock.h> 1339b9004dSPhilipp Zabel #include <linux/delay.h> 1439b9004dSPhilipp Zabel #include <linux/interrupt.h> 1539b9004dSPhilipp Zabel #include <linux/io.h> 1639b9004dSPhilipp Zabel #include <linux/clk.h> 1739b9004dSPhilipp Zabel #include <linux/list.h> 1839b9004dSPhilipp Zabel #include <linux/irq.h> 1939b9004dSPhilipp Zabel #include <linux/irqchip/chained_irq.h> 2039b9004dSPhilipp Zabel #include <linux/irqdomain.h> 2139b9004dSPhilipp Zabel #include <linux/of_device.h> 22304e6be6SPhilipp Zabel #include <linux/of_graph.h> 2339b9004dSPhilipp Zabel 2439b9004dSPhilipp Zabel #include <drm/drm_fourcc.h> 2539b9004dSPhilipp Zabel 2639b9004dSPhilipp Zabel #include <video/imx-ipu-v3.h> 2739b9004dSPhilipp Zabel #include "ipu-prv.h" 2839b9004dSPhilipp Zabel 2939b9004dSPhilipp Zabel static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset) 3039b9004dSPhilipp Zabel { 3139b9004dSPhilipp Zabel return readl(ipu->cm_reg + offset); 3239b9004dSPhilipp Zabel } 3339b9004dSPhilipp Zabel 3439b9004dSPhilipp Zabel static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset) 3539b9004dSPhilipp Zabel { 3639b9004dSPhilipp Zabel writel(value, ipu->cm_reg + offset); 3739b9004dSPhilipp Zabel } 3839b9004dSPhilipp Zabel 39572a7615SSteve Longerbeam int ipu_get_num(struct ipu_soc *ipu) 40572a7615SSteve Longerbeam { 41572a7615SSteve Longerbeam return ipu->id; 42572a7615SSteve Longerbeam } 43572a7615SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_get_num); 44572a7615SSteve Longerbeam 45f9bb7acbSPhilipp Zabel void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync) 4639b9004dSPhilipp Zabel { 4739b9004dSPhilipp Zabel u32 val; 4839b9004dSPhilipp Zabel 4939b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_SRM_PRI2); 50f9bb7acbSPhilipp Zabel val &= ~DP_S_SRM_MODE_MASK; 51f9bb7acbSPhilipp Zabel val |= sync ? DP_S_SRM_MODE_NEXT_FRAME : 52f9bb7acbSPhilipp Zabel DP_S_SRM_MODE_NOW; 5339b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_SRM_PRI2); 5439b9004dSPhilipp Zabel } 55f9bb7acbSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_srm_dp_update); 5639b9004dSPhilipp Zabel 5739b9004dSPhilipp Zabel enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) 5839b9004dSPhilipp Zabel { 5939b9004dSPhilipp Zabel switch (drm_fourcc) { 600cb8b757SPhilipp Zabel case DRM_FORMAT_ARGB1555: 610cb8b757SPhilipp Zabel case DRM_FORMAT_ABGR1555: 620cb8b757SPhilipp Zabel case DRM_FORMAT_RGBA5551: 630cb8b757SPhilipp Zabel case DRM_FORMAT_BGRA5551: 6439b9004dSPhilipp Zabel case DRM_FORMAT_RGB565: 6539b9004dSPhilipp Zabel case DRM_FORMAT_BGR565: 6639b9004dSPhilipp Zabel case DRM_FORMAT_RGB888: 6739b9004dSPhilipp Zabel case DRM_FORMAT_BGR888: 687d2e8a20SLucas Stach case DRM_FORMAT_ARGB4444: 6939b9004dSPhilipp Zabel case DRM_FORMAT_XRGB8888: 7039b9004dSPhilipp Zabel case DRM_FORMAT_XBGR8888: 7139b9004dSPhilipp Zabel case DRM_FORMAT_RGBX8888: 7239b9004dSPhilipp Zabel case DRM_FORMAT_BGRX8888: 7339b9004dSPhilipp Zabel case DRM_FORMAT_ARGB8888: 7439b9004dSPhilipp Zabel case DRM_FORMAT_ABGR8888: 7539b9004dSPhilipp Zabel case DRM_FORMAT_RGBA8888: 7639b9004dSPhilipp Zabel case DRM_FORMAT_BGRA8888: 77e72db3b1SPhilipp Zabel case DRM_FORMAT_RGB565_A8: 78e72db3b1SPhilipp Zabel case DRM_FORMAT_BGR565_A8: 79e72db3b1SPhilipp Zabel case DRM_FORMAT_RGB888_A8: 80e72db3b1SPhilipp Zabel case DRM_FORMAT_BGR888_A8: 81e72db3b1SPhilipp Zabel case DRM_FORMAT_RGBX8888_A8: 82e72db3b1SPhilipp Zabel case DRM_FORMAT_BGRX8888_A8: 8339b9004dSPhilipp Zabel return IPUV3_COLORSPACE_RGB; 8439b9004dSPhilipp Zabel case DRM_FORMAT_YUYV: 8539b9004dSPhilipp Zabel case DRM_FORMAT_UYVY: 8639b9004dSPhilipp Zabel case DRM_FORMAT_YUV420: 8739b9004dSPhilipp Zabel case DRM_FORMAT_YVU420: 889a34cef0SSteve Longerbeam case DRM_FORMAT_YUV422: 899a34cef0SSteve Longerbeam case DRM_FORMAT_YVU422: 90c9d508c2SPhilipp Zabel case DRM_FORMAT_YUV444: 91c9d508c2SPhilipp Zabel case DRM_FORMAT_YVU444: 929a34cef0SSteve Longerbeam case DRM_FORMAT_NV12: 939a34cef0SSteve Longerbeam case DRM_FORMAT_NV21: 949a34cef0SSteve Longerbeam case DRM_FORMAT_NV16: 959a34cef0SSteve Longerbeam case DRM_FORMAT_NV61: 9639b9004dSPhilipp Zabel return IPUV3_COLORSPACE_YUV; 9739b9004dSPhilipp Zabel default: 9839b9004dSPhilipp Zabel return IPUV3_COLORSPACE_UNKNOWN; 9939b9004dSPhilipp Zabel } 10039b9004dSPhilipp Zabel } 10139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace); 10239b9004dSPhilipp Zabel 10339b9004dSPhilipp Zabel enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) 10439b9004dSPhilipp Zabel { 10539b9004dSPhilipp Zabel switch (pixelformat) { 10639b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUV420: 10739b9004dSPhilipp Zabel case V4L2_PIX_FMT_YVU420: 1089a34cef0SSteve Longerbeam case V4L2_PIX_FMT_YUV422P: 10939b9004dSPhilipp Zabel case V4L2_PIX_FMT_UYVY: 11039b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUYV: 1119a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV12: 1129a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV21: 1139a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV16: 1149a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV61: 11539b9004dSPhilipp Zabel return IPUV3_COLORSPACE_YUV; 1165c41bb60SPhilipp Zabel case V4L2_PIX_FMT_XRGB32: 1175c41bb60SPhilipp Zabel case V4L2_PIX_FMT_XBGR32: 11839b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB32: 11939b9004dSPhilipp Zabel case V4L2_PIX_FMT_BGR32: 12039b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB24: 12139b9004dSPhilipp Zabel case V4L2_PIX_FMT_BGR24: 12239b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB565: 12339b9004dSPhilipp Zabel return IPUV3_COLORSPACE_RGB; 12439b9004dSPhilipp Zabel default: 12539b9004dSPhilipp Zabel return IPUV3_COLORSPACE_UNKNOWN; 12639b9004dSPhilipp Zabel } 12739b9004dSPhilipp Zabel } 12839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace); 12939b9004dSPhilipp Zabel 1304cea940dSSteve Longerbeam bool ipu_pixelformat_is_planar(u32 pixelformat) 1314cea940dSSteve Longerbeam { 1324cea940dSSteve Longerbeam switch (pixelformat) { 1334cea940dSSteve Longerbeam case V4L2_PIX_FMT_YUV420: 1344cea940dSSteve Longerbeam case V4L2_PIX_FMT_YVU420: 1359a34cef0SSteve Longerbeam case V4L2_PIX_FMT_YUV422P: 1369a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV12: 1379a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV21: 1389a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV16: 1399a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV61: 1404cea940dSSteve Longerbeam return true; 1414cea940dSSteve Longerbeam } 1424cea940dSSteve Longerbeam 1434cea940dSSteve Longerbeam return false; 1444cea940dSSteve Longerbeam } 1454cea940dSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar); 1464cea940dSSteve Longerbeam 147ae0e9708SSteve Longerbeam enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code) 148ae0e9708SSteve Longerbeam { 149ae0e9708SSteve Longerbeam switch (mbus_code & 0xf000) { 150ae0e9708SSteve Longerbeam case 0x1000: 151ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_RGB; 152ae0e9708SSteve Longerbeam case 0x2000: 153ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_YUV; 154ae0e9708SSteve Longerbeam default: 155ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_UNKNOWN; 156ae0e9708SSteve Longerbeam } 157ae0e9708SSteve Longerbeam } 158ae0e9708SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace); 159ae0e9708SSteve Longerbeam 1606930afdcSSteve Longerbeam int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat) 1616930afdcSSteve Longerbeam { 1626930afdcSSteve Longerbeam switch (pixelformat) { 1636930afdcSSteve Longerbeam case V4L2_PIX_FMT_YUV420: 1646930afdcSSteve Longerbeam case V4L2_PIX_FMT_YVU420: 1659a34cef0SSteve Longerbeam case V4L2_PIX_FMT_YUV422P: 1669a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV12: 1679a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV21: 1689a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV16: 1699a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV61: 1706930afdcSSteve Longerbeam /* 1716930afdcSSteve Longerbeam * for the planar YUV formats, the stride passed to 1726930afdcSSteve Longerbeam * cpmem must be the stride in bytes of the Y plane. 1736930afdcSSteve Longerbeam * And all the planar YUV formats have an 8-bit 1746930afdcSSteve Longerbeam * Y component. 1756930afdcSSteve Longerbeam */ 1766930afdcSSteve Longerbeam return (8 * pixel_stride) >> 3; 1776930afdcSSteve Longerbeam case V4L2_PIX_FMT_RGB565: 1786930afdcSSteve Longerbeam case V4L2_PIX_FMT_YUYV: 1796930afdcSSteve Longerbeam case V4L2_PIX_FMT_UYVY: 1806930afdcSSteve Longerbeam return (16 * pixel_stride) >> 3; 1816930afdcSSteve Longerbeam case V4L2_PIX_FMT_BGR24: 1826930afdcSSteve Longerbeam case V4L2_PIX_FMT_RGB24: 1836930afdcSSteve Longerbeam return (24 * pixel_stride) >> 3; 1846930afdcSSteve Longerbeam case V4L2_PIX_FMT_BGR32: 1856930afdcSSteve Longerbeam case V4L2_PIX_FMT_RGB32: 1865c41bb60SPhilipp Zabel case V4L2_PIX_FMT_XBGR32: 1875c41bb60SPhilipp Zabel case V4L2_PIX_FMT_XRGB32: 1886930afdcSSteve Longerbeam return (32 * pixel_stride) >> 3; 1896930afdcSSteve Longerbeam default: 1906930afdcSSteve Longerbeam break; 1916930afdcSSteve Longerbeam } 1926930afdcSSteve Longerbeam 1936930afdcSSteve Longerbeam return -EINVAL; 1946930afdcSSteve Longerbeam } 1956930afdcSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_stride_to_bytes); 1966930afdcSSteve Longerbeam 197f835f386SSteve Longerbeam int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees, 198f835f386SSteve Longerbeam bool hflip, bool vflip) 199f835f386SSteve Longerbeam { 200f835f386SSteve Longerbeam u32 r90, vf, hf; 201f835f386SSteve Longerbeam 202f835f386SSteve Longerbeam switch (degrees) { 203f835f386SSteve Longerbeam case 0: 204f835f386SSteve Longerbeam vf = hf = r90 = 0; 205f835f386SSteve Longerbeam break; 206f835f386SSteve Longerbeam case 90: 207f835f386SSteve Longerbeam vf = hf = 0; 208f835f386SSteve Longerbeam r90 = 1; 209f835f386SSteve Longerbeam break; 210f835f386SSteve Longerbeam case 180: 211f835f386SSteve Longerbeam vf = hf = 1; 212f835f386SSteve Longerbeam r90 = 0; 213f835f386SSteve Longerbeam break; 214f835f386SSteve Longerbeam case 270: 215f835f386SSteve Longerbeam vf = hf = r90 = 1; 216f835f386SSteve Longerbeam break; 217f835f386SSteve Longerbeam default: 218f835f386SSteve Longerbeam return -EINVAL; 219f835f386SSteve Longerbeam } 220f835f386SSteve Longerbeam 221f835f386SSteve Longerbeam hf ^= (u32)hflip; 222f835f386SSteve Longerbeam vf ^= (u32)vflip; 223f835f386SSteve Longerbeam 224f835f386SSteve Longerbeam *mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf); 225f835f386SSteve Longerbeam return 0; 226f835f386SSteve Longerbeam } 227f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode); 228f835f386SSteve Longerbeam 229f835f386SSteve Longerbeam int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode, 230f835f386SSteve Longerbeam bool hflip, bool vflip) 231f835f386SSteve Longerbeam { 232f835f386SSteve Longerbeam u32 r90, vf, hf; 233f835f386SSteve Longerbeam 234f835f386SSteve Longerbeam r90 = ((u32)mode >> 2) & 0x1; 235f835f386SSteve Longerbeam hf = ((u32)mode >> 1) & 0x1; 236f835f386SSteve Longerbeam vf = ((u32)mode >> 0) & 0x1; 237f835f386SSteve Longerbeam hf ^= (u32)hflip; 238f835f386SSteve Longerbeam vf ^= (u32)vflip; 239f835f386SSteve Longerbeam 240f835f386SSteve Longerbeam switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) { 241f835f386SSteve Longerbeam case IPU_ROTATE_NONE: 242f835f386SSteve Longerbeam *degrees = 0; 243f835f386SSteve Longerbeam break; 244f835f386SSteve Longerbeam case IPU_ROTATE_90_RIGHT: 245f835f386SSteve Longerbeam *degrees = 90; 246f835f386SSteve Longerbeam break; 247f835f386SSteve Longerbeam case IPU_ROTATE_180: 248f835f386SSteve Longerbeam *degrees = 180; 249f835f386SSteve Longerbeam break; 250f835f386SSteve Longerbeam case IPU_ROTATE_90_LEFT: 251f835f386SSteve Longerbeam *degrees = 270; 252f835f386SSteve Longerbeam break; 253f835f386SSteve Longerbeam default: 254f835f386SSteve Longerbeam return -EINVAL; 255f835f386SSteve Longerbeam } 256f835f386SSteve Longerbeam 257f835f386SSteve Longerbeam return 0; 258f835f386SSteve Longerbeam } 259f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees); 260f835f386SSteve Longerbeam 26139b9004dSPhilipp Zabel struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) 26239b9004dSPhilipp Zabel { 26339b9004dSPhilipp Zabel struct ipuv3_channel *channel; 26439b9004dSPhilipp Zabel 26539b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, num); 26639b9004dSPhilipp Zabel 26739b9004dSPhilipp Zabel if (num > 63) 26839b9004dSPhilipp Zabel return ERR_PTR(-ENODEV); 26939b9004dSPhilipp Zabel 27039b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock); 27139b9004dSPhilipp Zabel 27293adc8b5SPhilipp Zabel list_for_each_entry(channel, &ipu->channels, list) { 27393adc8b5SPhilipp Zabel if (channel->num == num) { 27439b9004dSPhilipp Zabel channel = ERR_PTR(-EBUSY); 27539b9004dSPhilipp Zabel goto out; 27639b9004dSPhilipp Zabel } 27793adc8b5SPhilipp Zabel } 27839b9004dSPhilipp Zabel 27993adc8b5SPhilipp Zabel channel = kzalloc(sizeof(*channel), GFP_KERNEL); 28093adc8b5SPhilipp Zabel if (!channel) { 28193adc8b5SPhilipp Zabel channel = ERR_PTR(-ENOMEM); 28293adc8b5SPhilipp Zabel goto out; 28393adc8b5SPhilipp Zabel } 28493adc8b5SPhilipp Zabel 28539b9004dSPhilipp Zabel channel->num = num; 28693adc8b5SPhilipp Zabel channel->ipu = ipu; 28793adc8b5SPhilipp Zabel list_add(&channel->list, &ipu->channels); 28839b9004dSPhilipp Zabel 28939b9004dSPhilipp Zabel out: 29039b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock); 29139b9004dSPhilipp Zabel 29239b9004dSPhilipp Zabel return channel; 29339b9004dSPhilipp Zabel } 29439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get); 29539b9004dSPhilipp Zabel 29639b9004dSPhilipp Zabel void ipu_idmac_put(struct ipuv3_channel *channel) 29739b9004dSPhilipp Zabel { 29839b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 29939b9004dSPhilipp Zabel 30039b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num); 30139b9004dSPhilipp Zabel 30239b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock); 30339b9004dSPhilipp Zabel 30493adc8b5SPhilipp Zabel list_del(&channel->list); 30593adc8b5SPhilipp Zabel kfree(channel); 30639b9004dSPhilipp Zabel 30739b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock); 30839b9004dSPhilipp Zabel } 30939b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_put); 31039b9004dSPhilipp Zabel 311aa52f578SSteve Longerbeam #define idma_mask(ch) (1 << ((ch) & 0x1f)) 31239b9004dSPhilipp Zabel 313e7268c69SSteve Longerbeam /* 314e7268c69SSteve Longerbeam * This is an undocumented feature, a write one to a channel bit in 315e7268c69SSteve Longerbeam * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's 316e7268c69SSteve Longerbeam * internal current buffer pointer so that transfers start from buffer 317e7268c69SSteve Longerbeam * 0 on the next channel enable (that's the theory anyway, the imx6 TRM 318e7268c69SSteve Longerbeam * only says these are read-only registers). This operation is required 319e7268c69SSteve Longerbeam * for channel linking to work correctly, for instance video capture 320e7268c69SSteve Longerbeam * pipelines that carry out image rotations will fail after the first 321e7268c69SSteve Longerbeam * streaming unless this function is called for each channel before 322e7268c69SSteve Longerbeam * re-enabling the channels. 323e7268c69SSteve Longerbeam */ 324e7268c69SSteve Longerbeam static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel) 325e7268c69SSteve Longerbeam { 326e7268c69SSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 327e7268c69SSteve Longerbeam unsigned int chno = channel->num; 328e7268c69SSteve Longerbeam 329e7268c69SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno)); 330e7268c69SSteve Longerbeam } 331e7268c69SSteve Longerbeam 33239b9004dSPhilipp Zabel void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, 33339b9004dSPhilipp Zabel bool doublebuffer) 33439b9004dSPhilipp Zabel { 33539b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 33639b9004dSPhilipp Zabel unsigned long flags; 33739b9004dSPhilipp Zabel u32 reg; 33839b9004dSPhilipp Zabel 33939b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 34039b9004dSPhilipp Zabel 34139b9004dSPhilipp Zabel reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 34239b9004dSPhilipp Zabel if (doublebuffer) 34339b9004dSPhilipp Zabel reg |= idma_mask(channel->num); 34439b9004dSPhilipp Zabel else 34539b9004dSPhilipp Zabel reg &= ~idma_mask(channel->num); 34639b9004dSPhilipp Zabel ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num)); 34739b9004dSPhilipp Zabel 348e7268c69SSteve Longerbeam __ipu_idmac_reset_current_buffer(channel); 349e7268c69SSteve Longerbeam 35039b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 35139b9004dSPhilipp Zabel } 35239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer); 35339b9004dSPhilipp Zabel 3544fd1a07aSSteve Longerbeam static const struct { 3554fd1a07aSSteve Longerbeam int chnum; 3564fd1a07aSSteve Longerbeam u32 reg; 3574fd1a07aSSteve Longerbeam int shift; 3584fd1a07aSSteve Longerbeam } idmac_lock_en_info[] = { 3594fd1a07aSSteve Longerbeam { .chnum = 5, .reg = IDMAC_CH_LOCK_EN_1, .shift = 0, }, 3604fd1a07aSSteve Longerbeam { .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift = 2, }, 3614fd1a07aSSteve Longerbeam { .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift = 4, }, 3624fd1a07aSSteve Longerbeam { .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift = 6, }, 3634fd1a07aSSteve Longerbeam { .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift = 8, }, 3644fd1a07aSSteve Longerbeam { .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, }, 3654fd1a07aSSteve Longerbeam { .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, }, 3664fd1a07aSSteve Longerbeam { .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, }, 3674fd1a07aSSteve Longerbeam { .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, }, 3684fd1a07aSSteve Longerbeam { .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, }, 3694fd1a07aSSteve Longerbeam { .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, }, 3704fd1a07aSSteve Longerbeam { .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift = 0, }, 3714fd1a07aSSteve Longerbeam { .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift = 2, }, 3724fd1a07aSSteve Longerbeam { .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift = 4, }, 3734fd1a07aSSteve Longerbeam { .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift = 6, }, 3744fd1a07aSSteve Longerbeam { .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift = 8, }, 3754fd1a07aSSteve Longerbeam { .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, }, 3764fd1a07aSSteve Longerbeam }; 3774fd1a07aSSteve Longerbeam 3784fd1a07aSSteve Longerbeam int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts) 3794fd1a07aSSteve Longerbeam { 3804fd1a07aSSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 3814fd1a07aSSteve Longerbeam unsigned long flags; 3824fd1a07aSSteve Longerbeam u32 bursts, regval; 3834fd1a07aSSteve Longerbeam int i; 3844fd1a07aSSteve Longerbeam 3854fd1a07aSSteve Longerbeam switch (num_bursts) { 3864fd1a07aSSteve Longerbeam case 0: 3874fd1a07aSSteve Longerbeam case 1: 3884fd1a07aSSteve Longerbeam bursts = 0x00; /* locking disabled */ 3894fd1a07aSSteve Longerbeam break; 3904fd1a07aSSteve Longerbeam case 2: 3914fd1a07aSSteve Longerbeam bursts = 0x01; 3924fd1a07aSSteve Longerbeam break; 3934fd1a07aSSteve Longerbeam case 4: 3944fd1a07aSSteve Longerbeam bursts = 0x02; 3954fd1a07aSSteve Longerbeam break; 3964fd1a07aSSteve Longerbeam case 8: 3974fd1a07aSSteve Longerbeam bursts = 0x03; 3984fd1a07aSSteve Longerbeam break; 3994fd1a07aSSteve Longerbeam default: 4004fd1a07aSSteve Longerbeam return -EINVAL; 4014fd1a07aSSteve Longerbeam } 4024fd1a07aSSteve Longerbeam 403cda77556SPhilipp Zabel /* 404cda77556SPhilipp Zabel * IPUv3EX / i.MX51 has a different register layout, and on IPUv3M / 405cda77556SPhilipp Zabel * i.MX53 channel arbitration locking doesn't seem to work properly. 406cda77556SPhilipp Zabel * Allow enabling the lock feature on IPUv3H / i.MX6 only. 407cda77556SPhilipp Zabel */ 408cda77556SPhilipp Zabel if (bursts && ipu->ipu_type != IPUV3H) 409cda77556SPhilipp Zabel return -EINVAL; 410cda77556SPhilipp Zabel 4114fd1a07aSSteve Longerbeam for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) { 4124fd1a07aSSteve Longerbeam if (channel->num == idmac_lock_en_info[i].chnum) 4134fd1a07aSSteve Longerbeam break; 4144fd1a07aSSteve Longerbeam } 4154fd1a07aSSteve Longerbeam if (i >= ARRAY_SIZE(idmac_lock_en_info)) 4164fd1a07aSSteve Longerbeam return -EINVAL; 4174fd1a07aSSteve Longerbeam 4184fd1a07aSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 4194fd1a07aSSteve Longerbeam 4204fd1a07aSSteve Longerbeam regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg); 4214fd1a07aSSteve Longerbeam regval &= ~(0x03 << idmac_lock_en_info[i].shift); 4224fd1a07aSSteve Longerbeam regval |= (bursts << idmac_lock_en_info[i].shift); 4234fd1a07aSSteve Longerbeam ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg); 4244fd1a07aSSteve Longerbeam 4254fd1a07aSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 4264fd1a07aSSteve Longerbeam 4274fd1a07aSSteve Longerbeam return 0; 4284fd1a07aSSteve Longerbeam } 4294fd1a07aSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable); 4304fd1a07aSSteve Longerbeam 43139b9004dSPhilipp Zabel int ipu_module_enable(struct ipu_soc *ipu, u32 mask) 43239b9004dSPhilipp Zabel { 43339b9004dSPhilipp Zabel unsigned long lock_flags; 43439b9004dSPhilipp Zabel u32 val; 43539b9004dSPhilipp Zabel 43639b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags); 43739b9004dSPhilipp Zabel 43839b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN); 43939b9004dSPhilipp Zabel 44039b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN) 44139b9004dSPhilipp Zabel val |= IPU_DI0_COUNTER_RELEASE; 44239b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN) 44339b9004dSPhilipp Zabel val |= IPU_DI1_COUNTER_RELEASE; 44439b9004dSPhilipp Zabel 44539b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN); 44639b9004dSPhilipp Zabel 44739b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF); 44839b9004dSPhilipp Zabel val |= mask; 44939b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF); 45039b9004dSPhilipp Zabel 45139b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags); 45239b9004dSPhilipp Zabel 45339b9004dSPhilipp Zabel return 0; 45439b9004dSPhilipp Zabel } 45539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_enable); 45639b9004dSPhilipp Zabel 45739b9004dSPhilipp Zabel int ipu_module_disable(struct ipu_soc *ipu, u32 mask) 45839b9004dSPhilipp Zabel { 45939b9004dSPhilipp Zabel unsigned long lock_flags; 46039b9004dSPhilipp Zabel u32 val; 46139b9004dSPhilipp Zabel 46239b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags); 46339b9004dSPhilipp Zabel 46439b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF); 46539b9004dSPhilipp Zabel val &= ~mask; 46639b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF); 46739b9004dSPhilipp Zabel 46839b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN); 46939b9004dSPhilipp Zabel 47039b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN) 47139b9004dSPhilipp Zabel val &= ~IPU_DI0_COUNTER_RELEASE; 47239b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN) 47339b9004dSPhilipp Zabel val &= ~IPU_DI1_COUNTER_RELEASE; 47439b9004dSPhilipp Zabel 47539b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN); 47639b9004dSPhilipp Zabel 47739b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags); 47839b9004dSPhilipp Zabel 47939b9004dSPhilipp Zabel return 0; 48039b9004dSPhilipp Zabel } 48139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_disable); 48239b9004dSPhilipp Zabel 483e9046097SPhilipp Zabel int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel) 484e9046097SPhilipp Zabel { 485e9046097SPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 486e9046097SPhilipp Zabel unsigned int chno = channel->num; 487e9046097SPhilipp Zabel 488e9046097SPhilipp Zabel return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0; 489e9046097SPhilipp Zabel } 490e9046097SPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer); 491e9046097SPhilipp Zabel 492aa52f578SSteve Longerbeam bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num) 493aa52f578SSteve Longerbeam { 494aa52f578SSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 495aa52f578SSteve Longerbeam unsigned long flags; 496aa52f578SSteve Longerbeam u32 reg = 0; 497aa52f578SSteve Longerbeam 498aa52f578SSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 499aa52f578SSteve Longerbeam switch (buf_num) { 500aa52f578SSteve Longerbeam case 0: 501aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)); 502aa52f578SSteve Longerbeam break; 503aa52f578SSteve Longerbeam case 1: 504aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)); 505aa52f578SSteve Longerbeam break; 506aa52f578SSteve Longerbeam case 2: 507aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num)); 508aa52f578SSteve Longerbeam break; 509aa52f578SSteve Longerbeam } 510aa52f578SSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 511aa52f578SSteve Longerbeam 512aa52f578SSteve Longerbeam return ((reg & idma_mask(channel->num)) != 0); 513aa52f578SSteve Longerbeam } 514aa52f578SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready); 515aa52f578SSteve Longerbeam 51639b9004dSPhilipp Zabel void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num) 51739b9004dSPhilipp Zabel { 51839b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 51939b9004dSPhilipp Zabel unsigned int chno = channel->num; 52039b9004dSPhilipp Zabel unsigned long flags; 52139b9004dSPhilipp Zabel 52239b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 52339b9004dSPhilipp Zabel 52439b9004dSPhilipp Zabel /* Mark buffer as ready. */ 52539b9004dSPhilipp Zabel if (buf_num == 0) 52639b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); 52739b9004dSPhilipp Zabel else 52839b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); 52939b9004dSPhilipp Zabel 53039b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 53139b9004dSPhilipp Zabel } 53239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer); 53339b9004dSPhilipp Zabel 534bce6f087SSteve Longerbeam void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num) 535bce6f087SSteve Longerbeam { 536bce6f087SSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 537bce6f087SSteve Longerbeam unsigned int chno = channel->num; 538bce6f087SSteve Longerbeam unsigned long flags; 539bce6f087SSteve Longerbeam 540bce6f087SSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 541bce6f087SSteve Longerbeam 542bce6f087SSteve Longerbeam ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */ 543bce6f087SSteve Longerbeam switch (buf_num) { 544bce6f087SSteve Longerbeam case 0: 545bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); 546bce6f087SSteve Longerbeam break; 547bce6f087SSteve Longerbeam case 1: 548bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); 549bce6f087SSteve Longerbeam break; 550bce6f087SSteve Longerbeam case 2: 551bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno)); 552bce6f087SSteve Longerbeam break; 553bce6f087SSteve Longerbeam default: 554bce6f087SSteve Longerbeam break; 555bce6f087SSteve Longerbeam } 556bce6f087SSteve Longerbeam ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ 557bce6f087SSteve Longerbeam 558bce6f087SSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 559bce6f087SSteve Longerbeam } 560bce6f087SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer); 561bce6f087SSteve Longerbeam 56239b9004dSPhilipp Zabel int ipu_idmac_enable_channel(struct ipuv3_channel *channel) 56339b9004dSPhilipp Zabel { 56439b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 56539b9004dSPhilipp Zabel u32 val; 56639b9004dSPhilipp Zabel unsigned long flags; 56739b9004dSPhilipp Zabel 56839b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 56939b9004dSPhilipp Zabel 57039b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 57139b9004dSPhilipp Zabel val |= idma_mask(channel->num); 57239b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 57339b9004dSPhilipp Zabel 57439b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 57539b9004dSPhilipp Zabel 57639b9004dSPhilipp Zabel return 0; 57739b9004dSPhilipp Zabel } 57839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); 57939b9004dSPhilipp Zabel 580682b7c1cSLinus Torvalds bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) 581682b7c1cSLinus Torvalds { 582682b7c1cSLinus Torvalds return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); 583682b7c1cSLinus Torvalds } 584682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); 585682b7c1cSLinus Torvalds 58639b9004dSPhilipp Zabel int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) 58739b9004dSPhilipp Zabel { 58839b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 58939b9004dSPhilipp Zabel unsigned long timeout; 59039b9004dSPhilipp Zabel 59139b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(ms); 59239b9004dSPhilipp Zabel while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) & 59339b9004dSPhilipp Zabel idma_mask(channel->num)) { 59439b9004dSPhilipp Zabel if (time_after(jiffies, timeout)) 59539b9004dSPhilipp Zabel return -ETIMEDOUT; 59639b9004dSPhilipp Zabel cpu_relax(); 59739b9004dSPhilipp Zabel } 59839b9004dSPhilipp Zabel 59939b9004dSPhilipp Zabel return 0; 60039b9004dSPhilipp Zabel } 60139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); 60239b9004dSPhilipp Zabel 60339b9004dSPhilipp Zabel int ipu_idmac_disable_channel(struct ipuv3_channel *channel) 60439b9004dSPhilipp Zabel { 60539b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 60639b9004dSPhilipp Zabel u32 val; 60739b9004dSPhilipp Zabel unsigned long flags; 60839b9004dSPhilipp Zabel 60939b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 61039b9004dSPhilipp Zabel 61139b9004dSPhilipp Zabel /* Disable DMA channel(s) */ 61239b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 61339b9004dSPhilipp Zabel val &= ~idma_mask(channel->num); 61439b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 61539b9004dSPhilipp Zabel 616e7268c69SSteve Longerbeam __ipu_idmac_reset_current_buffer(channel); 617e7268c69SSteve Longerbeam 61839b9004dSPhilipp Zabel /* Set channel buffers NOT to be ready */ 61939b9004dSPhilipp Zabel ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */ 62039b9004dSPhilipp Zabel 62139b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) & 62239b9004dSPhilipp Zabel idma_mask(channel->num)) { 62339b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num), 62439b9004dSPhilipp Zabel IPU_CHA_BUF0_RDY(channel->num)); 62539b9004dSPhilipp Zabel } 62639b9004dSPhilipp Zabel 62739b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) & 62839b9004dSPhilipp Zabel idma_mask(channel->num)) { 62939b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num), 63039b9004dSPhilipp Zabel IPU_CHA_BUF1_RDY(channel->num)); 63139b9004dSPhilipp Zabel } 63239b9004dSPhilipp Zabel 63339b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ 63439b9004dSPhilipp Zabel 63539b9004dSPhilipp Zabel /* Reset the double buffer */ 63639b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 63739b9004dSPhilipp Zabel val &= ~idma_mask(channel->num); 63839b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num)); 63939b9004dSPhilipp Zabel 64039b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 64139b9004dSPhilipp Zabel 64239b9004dSPhilipp Zabel return 0; 64339b9004dSPhilipp Zabel } 64439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel); 64539b9004dSPhilipp Zabel 6462bcf577eSSteve Longerbeam /* 6472bcf577eSSteve Longerbeam * The imx6 rev. D TRM says that enabling the WM feature will increase 6482bcf577eSSteve Longerbeam * a channel's priority. Refer to Table 36-8 Calculated priority value. 6492bcf577eSSteve Longerbeam * The sub-module that is the sink or source for the channel must enable 6502bcf577eSSteve Longerbeam * watermark signal for this to take effect (SMFC_WM for instance). 6512bcf577eSSteve Longerbeam */ 6522bcf577eSSteve Longerbeam void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable) 6532bcf577eSSteve Longerbeam { 6542bcf577eSSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 6552bcf577eSSteve Longerbeam unsigned long flags; 6562bcf577eSSteve Longerbeam u32 val; 6572bcf577eSSteve Longerbeam 6582bcf577eSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 6592bcf577eSSteve Longerbeam 6602bcf577eSSteve Longerbeam val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num)); 6612bcf577eSSteve Longerbeam if (enable) 6622bcf577eSSteve Longerbeam val |= 1 << (channel->num % 32); 6632bcf577eSSteve Longerbeam else 6642bcf577eSSteve Longerbeam val &= ~(1 << (channel->num % 32)); 6652bcf577eSSteve Longerbeam ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num)); 6662bcf577eSSteve Longerbeam 6672bcf577eSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 6682bcf577eSSteve Longerbeam } 6692bcf577eSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark); 6702bcf577eSSteve Longerbeam 67139b9004dSPhilipp Zabel static int ipu_memory_reset(struct ipu_soc *ipu) 67239b9004dSPhilipp Zabel { 67339b9004dSPhilipp Zabel unsigned long timeout; 67439b9004dSPhilipp Zabel 67539b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); 67639b9004dSPhilipp Zabel 67739b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(1000); 67839b9004dSPhilipp Zabel while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { 67939b9004dSPhilipp Zabel if (time_after(jiffies, timeout)) 68039b9004dSPhilipp Zabel return -ETIME; 68139b9004dSPhilipp Zabel cpu_relax(); 68239b9004dSPhilipp Zabel } 68339b9004dSPhilipp Zabel 68439b9004dSPhilipp Zabel return 0; 68539b9004dSPhilipp Zabel } 68639b9004dSPhilipp Zabel 687ba07975fSSteve Longerbeam /* 688ba07975fSSteve Longerbeam * Set the source mux for the given CSI. Selects either parallel or 689ba07975fSSteve Longerbeam * MIPI CSI2 sources. 690ba07975fSSteve Longerbeam */ 691ba07975fSSteve Longerbeam void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2) 692ba07975fSSteve Longerbeam { 693ba07975fSSteve Longerbeam unsigned long flags; 694ba07975fSSteve Longerbeam u32 val, mask; 695ba07975fSSteve Longerbeam 696ba07975fSSteve Longerbeam mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE : 697ba07975fSSteve Longerbeam IPU_CONF_CSI0_DATA_SOURCE; 698ba07975fSSteve Longerbeam 699ba07975fSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 700ba07975fSSteve Longerbeam 701ba07975fSSteve Longerbeam val = ipu_cm_read(ipu, IPU_CONF); 702ba07975fSSteve Longerbeam if (mipi_csi2) 703ba07975fSSteve Longerbeam val |= mask; 704ba07975fSSteve Longerbeam else 705ba07975fSSteve Longerbeam val &= ~mask; 706ba07975fSSteve Longerbeam ipu_cm_write(ipu, val, IPU_CONF); 707ba07975fSSteve Longerbeam 708ba07975fSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 709ba07975fSSteve Longerbeam } 710ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux); 711ba07975fSSteve Longerbeam 712ba07975fSSteve Longerbeam /* 713ba07975fSSteve Longerbeam * Set the source mux for the IC. Selects either CSI[01] or the VDI. 714ba07975fSSteve Longerbeam */ 715ba07975fSSteve Longerbeam void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) 716ba07975fSSteve Longerbeam { 717ba07975fSSteve Longerbeam unsigned long flags; 718ba07975fSSteve Longerbeam u32 val; 719ba07975fSSteve Longerbeam 720ba07975fSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 721ba07975fSSteve Longerbeam 722ba07975fSSteve Longerbeam val = ipu_cm_read(ipu, IPU_CONF); 723b7dfee24SMarek Vasut if (vdi) 724ba07975fSSteve Longerbeam val |= IPU_CONF_IC_INPUT; 725b7dfee24SMarek Vasut else 726ba07975fSSteve Longerbeam val &= ~IPU_CONF_IC_INPUT; 727b7dfee24SMarek Vasut 728ba07975fSSteve Longerbeam if (csi_id == 1) 729ba07975fSSteve Longerbeam val |= IPU_CONF_CSI_SEL; 730ba07975fSSteve Longerbeam else 731ba07975fSSteve Longerbeam val &= ~IPU_CONF_CSI_SEL; 732b7dfee24SMarek Vasut 733ba07975fSSteve Longerbeam ipu_cm_write(ipu, val, IPU_CONF); 734ba07975fSSteve Longerbeam 735ba07975fSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 736ba07975fSSteve Longerbeam } 737ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux); 738ba07975fSSteve Longerbeam 739ac4708faSSteve Longerbeam 740ac4708faSSteve Longerbeam /* Frame Synchronization Unit Channel Linking */ 741ac4708faSSteve Longerbeam 742ac4708faSSteve Longerbeam struct fsu_link_reg_info { 743ac4708faSSteve Longerbeam int chno; 744ac4708faSSteve Longerbeam u32 reg; 745ac4708faSSteve Longerbeam u32 mask; 746ac4708faSSteve Longerbeam u32 val; 747ac4708faSSteve Longerbeam }; 748ac4708faSSteve Longerbeam 749ac4708faSSteve Longerbeam struct fsu_link_info { 750ac4708faSSteve Longerbeam struct fsu_link_reg_info src; 751ac4708faSSteve Longerbeam struct fsu_link_reg_info sink; 752ac4708faSSteve Longerbeam }; 753ac4708faSSteve Longerbeam 754ac4708faSSteve Longerbeam static const struct fsu_link_info fsu_link_info[] = { 755ac4708faSSteve Longerbeam { 756ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2, 757ac4708faSSteve Longerbeam FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC }, 758ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1, 759ac4708faSSteve Longerbeam FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC }, 760ac4708faSSteve Longerbeam }, { 761ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2, 762ac4708faSSteve Longerbeam FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF }, 763ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1, 764ac4708faSSteve Longerbeam FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF }, 765ac4708faSSteve Longerbeam }, { 766ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2, 767ac4708faSSteve Longerbeam FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP }, 768ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1, 769ac4708faSSteve Longerbeam FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP }, 770ac4708faSSteve Longerbeam }, { 771ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_CSI_DIRECT, 0 }, 772ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1, 773ac4708faSSteve Longerbeam FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT }, 774ac4708faSSteve Longerbeam }, 775ac4708faSSteve Longerbeam }; 776ac4708faSSteve Longerbeam 777ac4708faSSteve Longerbeam static const struct fsu_link_info *find_fsu_link_info(int src, int sink) 778ac4708faSSteve Longerbeam { 779ac4708faSSteve Longerbeam int i; 780ac4708faSSteve Longerbeam 781ac4708faSSteve Longerbeam for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) { 782ac4708faSSteve Longerbeam if (src == fsu_link_info[i].src.chno && 783ac4708faSSteve Longerbeam sink == fsu_link_info[i].sink.chno) 784ac4708faSSteve Longerbeam return &fsu_link_info[i]; 785ac4708faSSteve Longerbeam } 786ac4708faSSteve Longerbeam 787ac4708faSSteve Longerbeam return NULL; 788ac4708faSSteve Longerbeam } 789ac4708faSSteve Longerbeam 790ac4708faSSteve Longerbeam /* 791ac4708faSSteve Longerbeam * Links a source channel to a sink channel in the FSU. 792ac4708faSSteve Longerbeam */ 793ac4708faSSteve Longerbeam int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch) 794ac4708faSSteve Longerbeam { 795ac4708faSSteve Longerbeam const struct fsu_link_info *link; 796ac4708faSSteve Longerbeam u32 src_reg, sink_reg; 797ac4708faSSteve Longerbeam unsigned long flags; 798ac4708faSSteve Longerbeam 799ac4708faSSteve Longerbeam link = find_fsu_link_info(src_ch, sink_ch); 800ac4708faSSteve Longerbeam if (!link) 801ac4708faSSteve Longerbeam return -EINVAL; 802ac4708faSSteve Longerbeam 803ac4708faSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 804ac4708faSSteve Longerbeam 805ac4708faSSteve Longerbeam if (link->src.mask) { 806ac4708faSSteve Longerbeam src_reg = ipu_cm_read(ipu, link->src.reg); 807ac4708faSSteve Longerbeam src_reg &= ~link->src.mask; 808ac4708faSSteve Longerbeam src_reg |= link->src.val; 809ac4708faSSteve Longerbeam ipu_cm_write(ipu, src_reg, link->src.reg); 810ac4708faSSteve Longerbeam } 811ac4708faSSteve Longerbeam 812ac4708faSSteve Longerbeam if (link->sink.mask) { 813ac4708faSSteve Longerbeam sink_reg = ipu_cm_read(ipu, link->sink.reg); 814ac4708faSSteve Longerbeam sink_reg &= ~link->sink.mask; 815ac4708faSSteve Longerbeam sink_reg |= link->sink.val; 816ac4708faSSteve Longerbeam ipu_cm_write(ipu, sink_reg, link->sink.reg); 817ac4708faSSteve Longerbeam } 818ac4708faSSteve Longerbeam 819ac4708faSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 820ac4708faSSteve Longerbeam return 0; 821ac4708faSSteve Longerbeam } 822ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_link); 823ac4708faSSteve Longerbeam 824ac4708faSSteve Longerbeam /* 825ac4708faSSteve Longerbeam * Unlinks source and sink channels in the FSU. 826ac4708faSSteve Longerbeam */ 827ac4708faSSteve Longerbeam int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch) 828ac4708faSSteve Longerbeam { 829ac4708faSSteve Longerbeam const struct fsu_link_info *link; 830ac4708faSSteve Longerbeam u32 src_reg, sink_reg; 831ac4708faSSteve Longerbeam unsigned long flags; 832ac4708faSSteve Longerbeam 833ac4708faSSteve Longerbeam link = find_fsu_link_info(src_ch, sink_ch); 834ac4708faSSteve Longerbeam if (!link) 835ac4708faSSteve Longerbeam return -EINVAL; 836ac4708faSSteve Longerbeam 837ac4708faSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 838ac4708faSSteve Longerbeam 839ac4708faSSteve Longerbeam if (link->src.mask) { 840ac4708faSSteve Longerbeam src_reg = ipu_cm_read(ipu, link->src.reg); 841ac4708faSSteve Longerbeam src_reg &= ~link->src.mask; 842ac4708faSSteve Longerbeam ipu_cm_write(ipu, src_reg, link->src.reg); 843ac4708faSSteve Longerbeam } 844ac4708faSSteve Longerbeam 845ac4708faSSteve Longerbeam if (link->sink.mask) { 846ac4708faSSteve Longerbeam sink_reg = ipu_cm_read(ipu, link->sink.reg); 847ac4708faSSteve Longerbeam sink_reg &= ~link->sink.mask; 848ac4708faSSteve Longerbeam ipu_cm_write(ipu, sink_reg, link->sink.reg); 849ac4708faSSteve Longerbeam } 850ac4708faSSteve Longerbeam 851ac4708faSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 852ac4708faSSteve Longerbeam return 0; 853ac4708faSSteve Longerbeam } 854ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_unlink); 855ac4708faSSteve Longerbeam 856ac4708faSSteve Longerbeam /* Link IDMAC channels in the FSU */ 857ac4708faSSteve Longerbeam int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink) 858ac4708faSSteve Longerbeam { 859ac4708faSSteve Longerbeam return ipu_fsu_link(src->ipu, src->num, sink->num); 860ac4708faSSteve Longerbeam } 861ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_link); 862ac4708faSSteve Longerbeam 863ac4708faSSteve Longerbeam /* Unlink IDMAC channels in the FSU */ 864ac4708faSSteve Longerbeam int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink) 865ac4708faSSteve Longerbeam { 866ac4708faSSteve Longerbeam return ipu_fsu_unlink(src->ipu, src->num, sink->num); 867ac4708faSSteve Longerbeam } 868ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_unlink); 869ac4708faSSteve Longerbeam 87039b9004dSPhilipp Zabel struct ipu_devtype { 87139b9004dSPhilipp Zabel const char *name; 87239b9004dSPhilipp Zabel unsigned long cm_ofs; 87339b9004dSPhilipp Zabel unsigned long cpmem_ofs; 87439b9004dSPhilipp Zabel unsigned long srm_ofs; 87539b9004dSPhilipp Zabel unsigned long tpm_ofs; 8762ffd48f2SSteve Longerbeam unsigned long csi0_ofs; 8772ffd48f2SSteve Longerbeam unsigned long csi1_ofs; 8781aa8ea0dSSteve Longerbeam unsigned long ic_ofs; 87939b9004dSPhilipp Zabel unsigned long disp0_ofs; 88039b9004dSPhilipp Zabel unsigned long disp1_ofs; 88139b9004dSPhilipp Zabel unsigned long dc_tmpl_ofs; 88239b9004dSPhilipp Zabel unsigned long vdi_ofs; 88339b9004dSPhilipp Zabel enum ipuv3_type type; 88439b9004dSPhilipp Zabel }; 88539b9004dSPhilipp Zabel 88639b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx51 = { 88739b9004dSPhilipp Zabel .name = "IPUv3EX", 88839b9004dSPhilipp Zabel .cm_ofs = 0x1e000000, 88939b9004dSPhilipp Zabel .cpmem_ofs = 0x1f000000, 89039b9004dSPhilipp Zabel .srm_ofs = 0x1f040000, 89139b9004dSPhilipp Zabel .tpm_ofs = 0x1f060000, 8922c0408ddSAlexander Shiyan .csi0_ofs = 0x1e030000, 8932c0408ddSAlexander Shiyan .csi1_ofs = 0x1e038000, 894a49e7c0dSPhilipp Zabel .ic_ofs = 0x1e020000, 89539b9004dSPhilipp Zabel .disp0_ofs = 0x1e040000, 89639b9004dSPhilipp Zabel .disp1_ofs = 0x1e048000, 89739b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x1f080000, 89839b9004dSPhilipp Zabel .vdi_ofs = 0x1e068000, 89939b9004dSPhilipp Zabel .type = IPUV3EX, 90039b9004dSPhilipp Zabel }; 90139b9004dSPhilipp Zabel 90239b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx53 = { 90339b9004dSPhilipp Zabel .name = "IPUv3M", 90439b9004dSPhilipp Zabel .cm_ofs = 0x06000000, 90539b9004dSPhilipp Zabel .cpmem_ofs = 0x07000000, 90639b9004dSPhilipp Zabel .srm_ofs = 0x07040000, 90739b9004dSPhilipp Zabel .tpm_ofs = 0x07060000, 908bb867d21SSteve Longerbeam .csi0_ofs = 0x06030000, 909bb867d21SSteve Longerbeam .csi1_ofs = 0x06038000, 910a49e7c0dSPhilipp Zabel .ic_ofs = 0x06020000, 91139b9004dSPhilipp Zabel .disp0_ofs = 0x06040000, 91239b9004dSPhilipp Zabel .disp1_ofs = 0x06048000, 91339b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x07080000, 91439b9004dSPhilipp Zabel .vdi_ofs = 0x06068000, 91539b9004dSPhilipp Zabel .type = IPUV3M, 91639b9004dSPhilipp Zabel }; 91739b9004dSPhilipp Zabel 91839b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx6q = { 91939b9004dSPhilipp Zabel .name = "IPUv3H", 92039b9004dSPhilipp Zabel .cm_ofs = 0x00200000, 92139b9004dSPhilipp Zabel .cpmem_ofs = 0x00300000, 92239b9004dSPhilipp Zabel .srm_ofs = 0x00340000, 92339b9004dSPhilipp Zabel .tpm_ofs = 0x00360000, 9242ffd48f2SSteve Longerbeam .csi0_ofs = 0x00230000, 9252ffd48f2SSteve Longerbeam .csi1_ofs = 0x00238000, 9261aa8ea0dSSteve Longerbeam .ic_ofs = 0x00220000, 92739b9004dSPhilipp Zabel .disp0_ofs = 0x00240000, 92839b9004dSPhilipp Zabel .disp1_ofs = 0x00248000, 92939b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x00380000, 93039b9004dSPhilipp Zabel .vdi_ofs = 0x00268000, 93139b9004dSPhilipp Zabel .type = IPUV3H, 93239b9004dSPhilipp Zabel }; 93339b9004dSPhilipp Zabel 93439b9004dSPhilipp Zabel static const struct of_device_id imx_ipu_dt_ids[] = { 93539b9004dSPhilipp Zabel { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, 93639b9004dSPhilipp Zabel { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, 93739b9004dSPhilipp Zabel { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, 93892681fe7SLucas Stach { .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, }, 93939b9004dSPhilipp Zabel { /* sentinel */ } 94039b9004dSPhilipp Zabel }; 94139b9004dSPhilipp Zabel MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); 94239b9004dSPhilipp Zabel 94339b9004dSPhilipp Zabel static int ipu_submodules_init(struct ipu_soc *ipu, 94439b9004dSPhilipp Zabel struct platform_device *pdev, unsigned long ipu_base, 94539b9004dSPhilipp Zabel struct clk *ipu_clk) 94639b9004dSPhilipp Zabel { 94739b9004dSPhilipp Zabel char *unit; 94839b9004dSPhilipp Zabel int ret; 94939b9004dSPhilipp Zabel struct device *dev = &pdev->dev; 95039b9004dSPhilipp Zabel const struct ipu_devtype *devtype = ipu->devtype; 95139b9004dSPhilipp Zabel 9527d2691daSSteve Longerbeam ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs); 9537d2691daSSteve Longerbeam if (ret) { 9547d2691daSSteve Longerbeam unit = "cpmem"; 9557d2691daSSteve Longerbeam goto err_cpmem; 9567d2691daSSteve Longerbeam } 9577d2691daSSteve Longerbeam 9582ffd48f2SSteve Longerbeam ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs, 9592ffd48f2SSteve Longerbeam IPU_CONF_CSI0_EN, ipu_clk); 9602ffd48f2SSteve Longerbeam if (ret) { 9612ffd48f2SSteve Longerbeam unit = "csi0"; 9622ffd48f2SSteve Longerbeam goto err_csi_0; 9632ffd48f2SSteve Longerbeam } 9642ffd48f2SSteve Longerbeam 9652ffd48f2SSteve Longerbeam ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs, 9662ffd48f2SSteve Longerbeam IPU_CONF_CSI1_EN, ipu_clk); 9672ffd48f2SSteve Longerbeam if (ret) { 9682ffd48f2SSteve Longerbeam unit = "csi1"; 9692ffd48f2SSteve Longerbeam goto err_csi_1; 9702ffd48f2SSteve Longerbeam } 9712ffd48f2SSteve Longerbeam 9721aa8ea0dSSteve Longerbeam ret = ipu_ic_init(ipu, dev, 9731aa8ea0dSSteve Longerbeam ipu_base + devtype->ic_ofs, 9741aa8ea0dSSteve Longerbeam ipu_base + devtype->tpm_ofs); 9751aa8ea0dSSteve Longerbeam if (ret) { 9761aa8ea0dSSteve Longerbeam unit = "ic"; 9771aa8ea0dSSteve Longerbeam goto err_ic; 9781aa8ea0dSSteve Longerbeam } 9791aa8ea0dSSteve Longerbeam 9802d2ead45SSteve Longerbeam ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs, 9812d2ead45SSteve Longerbeam IPU_CONF_VDI_EN | IPU_CONF_ISP_EN | 9822d2ead45SSteve Longerbeam IPU_CONF_IC_INPUT); 9832d2ead45SSteve Longerbeam if (ret) { 9842d2ead45SSteve Longerbeam unit = "vdi"; 9852d2ead45SSteve Longerbeam goto err_vdi; 9862d2ead45SSteve Longerbeam } 9872d2ead45SSteve Longerbeam 988cd98e85aSSteve Longerbeam ret = ipu_image_convert_init(ipu, dev); 989cd98e85aSSteve Longerbeam if (ret) { 990cd98e85aSSteve Longerbeam unit = "image_convert"; 991cd98e85aSSteve Longerbeam goto err_image_convert; 992cd98e85aSSteve Longerbeam } 993cd98e85aSSteve Longerbeam 99439b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, 99539b9004dSPhilipp Zabel IPU_CONF_DI0_EN, ipu_clk); 99639b9004dSPhilipp Zabel if (ret) { 99739b9004dSPhilipp Zabel unit = "di0"; 99839b9004dSPhilipp Zabel goto err_di_0; 99939b9004dSPhilipp Zabel } 100039b9004dSPhilipp Zabel 100139b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs, 100239b9004dSPhilipp Zabel IPU_CONF_DI1_EN, ipu_clk); 100339b9004dSPhilipp Zabel if (ret) { 100439b9004dSPhilipp Zabel unit = "di1"; 100539b9004dSPhilipp Zabel goto err_di_1; 100639b9004dSPhilipp Zabel } 100739b9004dSPhilipp Zabel 100839b9004dSPhilipp Zabel ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs + 100939b9004dSPhilipp Zabel IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs); 101039b9004dSPhilipp Zabel if (ret) { 101139b9004dSPhilipp Zabel unit = "dc_template"; 101239b9004dSPhilipp Zabel goto err_dc; 101339b9004dSPhilipp Zabel } 101439b9004dSPhilipp Zabel 101539b9004dSPhilipp Zabel ret = ipu_dmfc_init(ipu, dev, ipu_base + 101639b9004dSPhilipp Zabel devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk); 101739b9004dSPhilipp Zabel if (ret) { 101839b9004dSPhilipp Zabel unit = "dmfc"; 101939b9004dSPhilipp Zabel goto err_dmfc; 102039b9004dSPhilipp Zabel } 102139b9004dSPhilipp Zabel 102239b9004dSPhilipp Zabel ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs); 102339b9004dSPhilipp Zabel if (ret) { 102439b9004dSPhilipp Zabel unit = "dp"; 102539b9004dSPhilipp Zabel goto err_dp; 102639b9004dSPhilipp Zabel } 102739b9004dSPhilipp Zabel 102835de925fSPhilipp Zabel ret = ipu_smfc_init(ipu, dev, ipu_base + 102935de925fSPhilipp Zabel devtype->cm_ofs + IPU_CM_SMFC_REG_OFS); 103035de925fSPhilipp Zabel if (ret) { 103135de925fSPhilipp Zabel unit = "smfc"; 103235de925fSPhilipp Zabel goto err_smfc; 103335de925fSPhilipp Zabel } 103435de925fSPhilipp Zabel 103539b9004dSPhilipp Zabel return 0; 103639b9004dSPhilipp Zabel 103735de925fSPhilipp Zabel err_smfc: 103835de925fSPhilipp Zabel ipu_dp_exit(ipu); 103939b9004dSPhilipp Zabel err_dp: 104039b9004dSPhilipp Zabel ipu_dmfc_exit(ipu); 104139b9004dSPhilipp Zabel err_dmfc: 104239b9004dSPhilipp Zabel ipu_dc_exit(ipu); 104339b9004dSPhilipp Zabel err_dc: 104439b9004dSPhilipp Zabel ipu_di_exit(ipu, 1); 104539b9004dSPhilipp Zabel err_di_1: 104639b9004dSPhilipp Zabel ipu_di_exit(ipu, 0); 104739b9004dSPhilipp Zabel err_di_0: 1048cd98e85aSSteve Longerbeam ipu_image_convert_exit(ipu); 1049cd98e85aSSteve Longerbeam err_image_convert: 10502d2ead45SSteve Longerbeam ipu_vdi_exit(ipu); 10512d2ead45SSteve Longerbeam err_vdi: 10521aa8ea0dSSteve Longerbeam ipu_ic_exit(ipu); 10531aa8ea0dSSteve Longerbeam err_ic: 10542ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 1); 10552ffd48f2SSteve Longerbeam err_csi_1: 10562ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 0); 10572ffd48f2SSteve Longerbeam err_csi_0: 10587d2691daSSteve Longerbeam ipu_cpmem_exit(ipu); 10597d2691daSSteve Longerbeam err_cpmem: 106039b9004dSPhilipp Zabel dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); 106139b9004dSPhilipp Zabel return ret; 106239b9004dSPhilipp Zabel } 106339b9004dSPhilipp Zabel 106439b9004dSPhilipp Zabel static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) 106539b9004dSPhilipp Zabel { 106639b9004dSPhilipp Zabel unsigned long status; 106739b9004dSPhilipp Zabel int i, bit, irq; 106839b9004dSPhilipp Zabel 106939b9004dSPhilipp Zabel for (i = 0; i < num_regs; i++) { 107039b9004dSPhilipp Zabel 107139b9004dSPhilipp Zabel status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i])); 107239b9004dSPhilipp Zabel status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i])); 107339b9004dSPhilipp Zabel 107439b9004dSPhilipp Zabel for_each_set_bit(bit, &status, 32) { 1075682b7c1cSLinus Torvalds irq = irq_linear_revmap(ipu->domain, 1076682b7c1cSLinus Torvalds regs[i] * 32 + bit); 107739b9004dSPhilipp Zabel if (irq) 107839b9004dSPhilipp Zabel generic_handle_irq(irq); 107939b9004dSPhilipp Zabel } 108039b9004dSPhilipp Zabel } 108139b9004dSPhilipp Zabel } 108239b9004dSPhilipp Zabel 1083bd0b9ac4SThomas Gleixner static void ipu_irq_handler(struct irq_desc *desc) 108439b9004dSPhilipp Zabel { 108539b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 10864d9efdfcSJiang Liu struct irq_chip *chip = irq_desc_get_chip(desc); 1087ac66b834SColin Ian King static const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14}; 108839b9004dSPhilipp Zabel 108939b9004dSPhilipp Zabel chained_irq_enter(chip, desc); 109039b9004dSPhilipp Zabel 109139b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 109239b9004dSPhilipp Zabel 109339b9004dSPhilipp Zabel chained_irq_exit(chip, desc); 109439b9004dSPhilipp Zabel } 109539b9004dSPhilipp Zabel 1096bd0b9ac4SThomas Gleixner static void ipu_err_irq_handler(struct irq_desc *desc) 109739b9004dSPhilipp Zabel { 109839b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 10994d9efdfcSJiang Liu struct irq_chip *chip = irq_desc_get_chip(desc); 1100ac66b834SColin Ian King static const int int_reg[] = { 4, 5, 8, 9}; 110139b9004dSPhilipp Zabel 110239b9004dSPhilipp Zabel chained_irq_enter(chip, desc); 110339b9004dSPhilipp Zabel 110439b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 110539b9004dSPhilipp Zabel 110639b9004dSPhilipp Zabel chained_irq_exit(chip, desc); 110739b9004dSPhilipp Zabel } 110839b9004dSPhilipp Zabel 1109682b7c1cSLinus Torvalds int ipu_map_irq(struct ipu_soc *ipu, int irq) 1110682b7c1cSLinus Torvalds { 1111682b7c1cSLinus Torvalds int virq; 1112682b7c1cSLinus Torvalds 1113682b7c1cSLinus Torvalds virq = irq_linear_revmap(ipu->domain, irq); 1114682b7c1cSLinus Torvalds if (!virq) 1115682b7c1cSLinus Torvalds virq = irq_create_mapping(ipu->domain, irq); 1116682b7c1cSLinus Torvalds 1117682b7c1cSLinus Torvalds return virq; 1118682b7c1cSLinus Torvalds } 1119682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_map_irq); 1120682b7c1cSLinus Torvalds 112139b9004dSPhilipp Zabel int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, 112239b9004dSPhilipp Zabel enum ipu_channel_irq irq_type) 112339b9004dSPhilipp Zabel { 1124682b7c1cSLinus Torvalds return ipu_map_irq(ipu, irq_type + channel->num); 112539b9004dSPhilipp Zabel } 112639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); 112739b9004dSPhilipp Zabel 112839b9004dSPhilipp Zabel static void ipu_submodules_exit(struct ipu_soc *ipu) 112939b9004dSPhilipp Zabel { 113035de925fSPhilipp Zabel ipu_smfc_exit(ipu); 113139b9004dSPhilipp Zabel ipu_dp_exit(ipu); 113239b9004dSPhilipp Zabel ipu_dmfc_exit(ipu); 113339b9004dSPhilipp Zabel ipu_dc_exit(ipu); 113439b9004dSPhilipp Zabel ipu_di_exit(ipu, 1); 113539b9004dSPhilipp Zabel ipu_di_exit(ipu, 0); 1136cd98e85aSSteve Longerbeam ipu_image_convert_exit(ipu); 11372d2ead45SSteve Longerbeam ipu_vdi_exit(ipu); 11381aa8ea0dSSteve Longerbeam ipu_ic_exit(ipu); 11392ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 1); 11402ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 0); 11417d2691daSSteve Longerbeam ipu_cpmem_exit(ipu); 114239b9004dSPhilipp Zabel } 114339b9004dSPhilipp Zabel 114439b9004dSPhilipp Zabel static int platform_remove_devices_fn(struct device *dev, void *unused) 114539b9004dSPhilipp Zabel { 114639b9004dSPhilipp Zabel struct platform_device *pdev = to_platform_device(dev); 114739b9004dSPhilipp Zabel 114839b9004dSPhilipp Zabel platform_device_unregister(pdev); 114939b9004dSPhilipp Zabel 115039b9004dSPhilipp Zabel return 0; 115139b9004dSPhilipp Zabel } 115239b9004dSPhilipp Zabel 115339b9004dSPhilipp Zabel static void platform_device_unregister_children(struct platform_device *pdev) 115439b9004dSPhilipp Zabel { 115539b9004dSPhilipp Zabel device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn); 115639b9004dSPhilipp Zabel } 115739b9004dSPhilipp Zabel 115839b9004dSPhilipp Zabel struct ipu_platform_reg { 115939b9004dSPhilipp Zabel struct ipu_client_platformdata pdata; 116039b9004dSPhilipp Zabel const char *name; 116139b9004dSPhilipp Zabel }; 116239b9004dSPhilipp Zabel 1163304e6be6SPhilipp Zabel /* These must be in the order of the corresponding device tree port nodes */ 1164310944d1SPhilipp Zabel static struct ipu_platform_reg client_reg[] = { 116539b9004dSPhilipp Zabel { 116639b9004dSPhilipp Zabel .pdata = { 1167304e6be6SPhilipp Zabel .csi = 0, 1168304e6be6SPhilipp Zabel .dma[0] = IPUV3_CHANNEL_CSI0, 1169304e6be6SPhilipp Zabel .dma[1] = -EINVAL, 1170304e6be6SPhilipp Zabel }, 117188287ec3SSteve Longerbeam .name = "imx-ipuv3-csi", 1172304e6be6SPhilipp Zabel }, { 1173304e6be6SPhilipp Zabel .pdata = { 1174304e6be6SPhilipp Zabel .csi = 1, 1175304e6be6SPhilipp Zabel .dma[0] = IPUV3_CHANNEL_CSI1, 1176304e6be6SPhilipp Zabel .dma[1] = -EINVAL, 1177304e6be6SPhilipp Zabel }, 117888287ec3SSteve Longerbeam .name = "imx-ipuv3-csi", 1179304e6be6SPhilipp Zabel }, { 1180304e6be6SPhilipp Zabel .pdata = { 118139b9004dSPhilipp Zabel .di = 0, 118239b9004dSPhilipp Zabel .dc = 5, 118339b9004dSPhilipp Zabel .dp = IPU_DP_FLOW_SYNC_BG, 118439b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC, 118539b9004dSPhilipp Zabel .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC, 118639b9004dSPhilipp Zabel }, 118739b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc", 118839b9004dSPhilipp Zabel }, { 118939b9004dSPhilipp Zabel .pdata = { 119039b9004dSPhilipp Zabel .di = 1, 119139b9004dSPhilipp Zabel .dc = 1, 119239b9004dSPhilipp Zabel .dp = -EINVAL, 119339b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC, 119439b9004dSPhilipp Zabel .dma[1] = -EINVAL, 119539b9004dSPhilipp Zabel }, 119639b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc", 119739b9004dSPhilipp Zabel }, 119839b9004dSPhilipp Zabel }; 119939b9004dSPhilipp Zabel 120039b9004dSPhilipp Zabel static DEFINE_MUTEX(ipu_client_id_mutex); 120139b9004dSPhilipp Zabel static int ipu_client_id; 120239b9004dSPhilipp Zabel 1203d6ca8ca7SPhilipp Zabel static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) 120439b9004dSPhilipp Zabel { 120539b9004dSPhilipp Zabel struct device *dev = ipu->dev; 120639b9004dSPhilipp Zabel unsigned i; 120739b9004dSPhilipp Zabel int id, ret; 120839b9004dSPhilipp Zabel 120939b9004dSPhilipp Zabel mutex_lock(&ipu_client_id_mutex); 121039b9004dSPhilipp Zabel id = ipu_client_id; 121139b9004dSPhilipp Zabel ipu_client_id += ARRAY_SIZE(client_reg); 121239b9004dSPhilipp Zabel mutex_unlock(&ipu_client_id_mutex); 121339b9004dSPhilipp Zabel 121439b9004dSPhilipp Zabel for (i = 0; i < ARRAY_SIZE(client_reg); i++) { 1215310944d1SPhilipp Zabel struct ipu_platform_reg *reg = &client_reg[i]; 121639b9004dSPhilipp Zabel struct platform_device *pdev; 121717e05217SPhilipp Zabel struct device_node *of_node; 121817e05217SPhilipp Zabel 121917e05217SPhilipp Zabel /* Associate subdevice with the corresponding port node */ 122017e05217SPhilipp Zabel of_node = of_graph_get_port_by_id(dev->of_node, i); 122117e05217SPhilipp Zabel if (!of_node) { 122217e05217SPhilipp Zabel dev_info(dev, 12234bf99144SRob Herring "no port@%d node in %pOF, not using %s%d\n", 12244bf99144SRob Herring i, dev->of_node, 122517e05217SPhilipp Zabel (i / 2) ? "DI" : "CSI", i % 2); 122617e05217SPhilipp Zabel continue; 122717e05217SPhilipp Zabel } 122839b9004dSPhilipp Zabel 1229304e6be6SPhilipp Zabel pdev = platform_device_alloc(reg->name, id++); 1230304e6be6SPhilipp Zabel if (!pdev) { 1231304e6be6SPhilipp Zabel ret = -ENOMEM; 1232304e6be6SPhilipp Zabel goto err_register; 1233304e6be6SPhilipp Zabel } 123439b9004dSPhilipp Zabel 1235304e6be6SPhilipp Zabel pdev->dev.parent = dev; 1236304e6be6SPhilipp Zabel 1237310944d1SPhilipp Zabel reg->pdata.of_node = of_node; 1238304e6be6SPhilipp Zabel ret = platform_device_add_data(pdev, ®->pdata, 1239304e6be6SPhilipp Zabel sizeof(reg->pdata)); 1240304e6be6SPhilipp Zabel if (!ret) 1241304e6be6SPhilipp Zabel ret = platform_device_add(pdev); 1242304e6be6SPhilipp Zabel if (ret) { 1243304e6be6SPhilipp Zabel platform_device_put(pdev); 124439b9004dSPhilipp Zabel goto err_register; 124539b9004dSPhilipp Zabel } 1246e4946cdcSAxel Lin } 124739b9004dSPhilipp Zabel 124839b9004dSPhilipp Zabel return 0; 124939b9004dSPhilipp Zabel 125039b9004dSPhilipp Zabel err_register: 125139b9004dSPhilipp Zabel platform_device_unregister_children(to_platform_device(dev)); 125239b9004dSPhilipp Zabel 125339b9004dSPhilipp Zabel return ret; 125439b9004dSPhilipp Zabel } 125539b9004dSPhilipp Zabel 125639b9004dSPhilipp Zabel 125739b9004dSPhilipp Zabel static int ipu_irq_init(struct ipu_soc *ipu) 125839b9004dSPhilipp Zabel { 125939b9004dSPhilipp Zabel struct irq_chip_generic *gc; 126039b9004dSPhilipp Zabel struct irq_chip_type *ct; 126139b9004dSPhilipp Zabel unsigned long unused[IPU_NUM_IRQS / 32] = { 126239b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 126339b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 126439b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 126539b9004dSPhilipp Zabel 0x4077ffff, 0xffe7e1fd, 126639b9004dSPhilipp Zabel 0x23fffffe, 0x8880fff0, 126739b9004dSPhilipp Zabel 0xf98fe7d0, 0xfff81fff, 126839b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 126939b9004dSPhilipp Zabel 0x00000000, 127039b9004dSPhilipp Zabel }; 127139b9004dSPhilipp Zabel int ret, i; 127239b9004dSPhilipp Zabel 127339b9004dSPhilipp Zabel ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS, 127439b9004dSPhilipp Zabel &irq_generic_chip_ops, ipu); 127539b9004dSPhilipp Zabel if (!ipu->domain) { 127639b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to add irq domain\n"); 127739b9004dSPhilipp Zabel return -ENODEV; 127839b9004dSPhilipp Zabel } 127939b9004dSPhilipp Zabel 128039b9004dSPhilipp Zabel ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", 1281ca0141deSRob Herring handle_level_irq, 0, 0, 0); 128239b9004dSPhilipp Zabel if (ret < 0) { 128339b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to alloc generic irq chips\n"); 128439b9004dSPhilipp Zabel irq_domain_remove(ipu->domain); 128539b9004dSPhilipp Zabel return ret; 128639b9004dSPhilipp Zabel } 128739b9004dSPhilipp Zabel 1288a92d8145SPhilipp Zabel /* Mask and clear all interrupts */ 1289a92d8145SPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i += 32) { 1290510e6426SRussell King ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32)); 1291a92d8145SPhilipp Zabel ipu_cm_write(ipu, ~unused[i / 32], IPU_INT_STAT(i / 32)); 1292a92d8145SPhilipp Zabel } 1293510e6426SRussell King 129439b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i += 32) { 129539b9004dSPhilipp Zabel gc = irq_get_domain_generic_chip(ipu->domain, i); 129639b9004dSPhilipp Zabel gc->reg_base = ipu->cm_reg; 129739b9004dSPhilipp Zabel gc->unused = unused[i / 32]; 129839b9004dSPhilipp Zabel ct = gc->chip_types; 129939b9004dSPhilipp Zabel ct->chip.irq_ack = irq_gc_ack_set_bit; 130039b9004dSPhilipp Zabel ct->chip.irq_mask = irq_gc_mask_clr_bit; 130139b9004dSPhilipp Zabel ct->chip.irq_unmask = irq_gc_mask_set_bit; 130239b9004dSPhilipp Zabel ct->regs.ack = IPU_INT_STAT(i / 32); 130339b9004dSPhilipp Zabel ct->regs.mask = IPU_INT_CTRL(i / 32); 130439b9004dSPhilipp Zabel } 130539b9004dSPhilipp Zabel 130686f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu); 130786f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler, 130886f5e733SRussell King ipu); 130939b9004dSPhilipp Zabel 131039b9004dSPhilipp Zabel return 0; 131139b9004dSPhilipp Zabel } 131239b9004dSPhilipp Zabel 131339b9004dSPhilipp Zabel static void ipu_irq_exit(struct ipu_soc *ipu) 131439b9004dSPhilipp Zabel { 131539b9004dSPhilipp Zabel int i, irq; 131639b9004dSPhilipp Zabel 131786f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL); 131886f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL); 131939b9004dSPhilipp Zabel 132039b9004dSPhilipp Zabel /* TODO: remove irq_domain_generic_chips */ 132139b9004dSPhilipp Zabel 132239b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i++) { 132339b9004dSPhilipp Zabel irq = irq_linear_revmap(ipu->domain, i); 132439b9004dSPhilipp Zabel if (irq) 132539b9004dSPhilipp Zabel irq_dispose_mapping(irq); 132639b9004dSPhilipp Zabel } 132739b9004dSPhilipp Zabel 132839b9004dSPhilipp Zabel irq_domain_remove(ipu->domain); 132939b9004dSPhilipp Zabel } 133039b9004dSPhilipp Zabel 13313feb049fSSteve Longerbeam void ipu_dump(struct ipu_soc *ipu) 13323feb049fSSteve Longerbeam { 13333feb049fSSteve Longerbeam int i; 13343feb049fSSteve Longerbeam 13353feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n", 13363feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CONF)); 13373feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n", 13383feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CONF)); 13393feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n", 13403feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_EN(0))); 13413feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n", 13423feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_EN(32))); 13433feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n", 13443feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_PRI(0))); 13453feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n", 13463feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_PRI(32))); 13473feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n", 13483feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_BAND_EN(0))); 13493feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n", 13503feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_BAND_EN(32))); 13513feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", 13523feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0))); 13533feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", 13543feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32))); 13553feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n", 13563feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW1)); 13573feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n", 13583feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW2)); 13593feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n", 13603feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW3)); 13613feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n", 13623feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_DISP_FLOW1)); 13633feb049fSSteve Longerbeam for (i = 0; i < 15; i++) 13643feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i, 13653feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_INT_CTRL(i))); 13663feb049fSSteve Longerbeam } 13673feb049fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_dump); 13683feb049fSSteve Longerbeam 136939b9004dSPhilipp Zabel static int ipu_probe(struct platform_device *pdev) 137039b9004dSPhilipp Zabel { 1371572a7615SSteve Longerbeam struct device_node *np = pdev->dev.of_node; 137239b9004dSPhilipp Zabel struct ipu_soc *ipu; 137339b9004dSPhilipp Zabel struct resource *res; 137439b9004dSPhilipp Zabel unsigned long ipu_base; 137593adc8b5SPhilipp Zabel int ret, irq_sync, irq_err; 137639b9004dSPhilipp Zabel const struct ipu_devtype *devtype; 137739b9004dSPhilipp Zabel 1378e92e4478SLABBE Corentin devtype = of_device_get_match_data(&pdev->dev); 1379e92e4478SLABBE Corentin if (!devtype) 1380e92e4478SLABBE Corentin return -EINVAL; 138139b9004dSPhilipp Zabel 138239b9004dSPhilipp Zabel irq_sync = platform_get_irq(pdev, 0); 138339b9004dSPhilipp Zabel irq_err = platform_get_irq(pdev, 1); 138439b9004dSPhilipp Zabel res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 138539b9004dSPhilipp Zabel 138639b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n", 138739b9004dSPhilipp Zabel irq_sync, irq_err); 138839b9004dSPhilipp Zabel 138939b9004dSPhilipp Zabel if (!res || irq_sync < 0 || irq_err < 0) 139039b9004dSPhilipp Zabel return -ENODEV; 139139b9004dSPhilipp Zabel 139239b9004dSPhilipp Zabel ipu_base = res->start; 139339b9004dSPhilipp Zabel 139439b9004dSPhilipp Zabel ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL); 139539b9004dSPhilipp Zabel if (!ipu) 139639b9004dSPhilipp Zabel return -ENODEV; 139739b9004dSPhilipp Zabel 139892681fe7SLucas Stach ipu->id = of_alias_get_id(np, "ipu"); 13992d87e6c1SPhilipp Zabel if (ipu->id < 0) 14002d87e6c1SPhilipp Zabel ipu->id = 0; 140192681fe7SLucas Stach 140230310c83SLucas Stach if (of_device_is_compatible(np, "fsl,imx6qp-ipu") && 140330310c83SLucas Stach IS_ENABLED(CONFIG_DRM)) { 140492681fe7SLucas Stach ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev, 140592681fe7SLucas Stach "fsl,prg", ipu->id); 140692681fe7SLucas Stach if (!ipu->prg_priv) 140792681fe7SLucas Stach return -EPROBE_DEFER; 140892681fe7SLucas Stach } 140992681fe7SLucas Stach 141039b9004dSPhilipp Zabel ipu->devtype = devtype; 141139b9004dSPhilipp Zabel ipu->ipu_type = devtype->type; 141239b9004dSPhilipp Zabel 141339b9004dSPhilipp Zabel spin_lock_init(&ipu->lock); 141439b9004dSPhilipp Zabel mutex_init(&ipu->channel_lock); 141593adc8b5SPhilipp Zabel INIT_LIST_HEAD(&ipu->channels); 141639b9004dSPhilipp Zabel 141739b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n", 141839b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs); 141939b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "idmac: 0x%08lx\n", 142039b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS); 142139b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n", 142239b9004dSPhilipp Zabel ipu_base + devtype->cpmem_ofs); 14232ffd48f2SSteve Longerbeam dev_dbg(&pdev->dev, "csi0: 0x%08lx\n", 14242ffd48f2SSteve Longerbeam ipu_base + devtype->csi0_ofs); 14252ffd48f2SSteve Longerbeam dev_dbg(&pdev->dev, "csi1: 0x%08lx\n", 14262ffd48f2SSteve Longerbeam ipu_base + devtype->csi1_ofs); 14271aa8ea0dSSteve Longerbeam dev_dbg(&pdev->dev, "ic: 0x%08lx\n", 14281aa8ea0dSSteve Longerbeam ipu_base + devtype->ic_ofs); 142939b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp0: 0x%08lx\n", 143039b9004dSPhilipp Zabel ipu_base + devtype->disp0_ofs); 143139b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp1: 0x%08lx\n", 143239b9004dSPhilipp Zabel ipu_base + devtype->disp1_ofs); 143339b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "srm: 0x%08lx\n", 143439b9004dSPhilipp Zabel ipu_base + devtype->srm_ofs); 143539b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "tpm: 0x%08lx\n", 143639b9004dSPhilipp Zabel ipu_base + devtype->tpm_ofs); 143739b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dc: 0x%08lx\n", 143839b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS); 143939b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "ic: 0x%08lx\n", 144039b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS); 144139b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n", 144239b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS); 144339b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "vdi: 0x%08lx\n", 144439b9004dSPhilipp Zabel ipu_base + devtype->vdi_ofs); 144539b9004dSPhilipp Zabel 144639b9004dSPhilipp Zabel ipu->cm_reg = devm_ioremap(&pdev->dev, 144739b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs, PAGE_SIZE); 144839b9004dSPhilipp Zabel ipu->idmac_reg = devm_ioremap(&pdev->dev, 144939b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, 145039b9004dSPhilipp Zabel PAGE_SIZE); 145139b9004dSPhilipp Zabel 14527d2691daSSteve Longerbeam if (!ipu->cm_reg || !ipu->idmac_reg) 145339b9004dSPhilipp Zabel return -ENOMEM; 145439b9004dSPhilipp Zabel 145539b9004dSPhilipp Zabel ipu->clk = devm_clk_get(&pdev->dev, "bus"); 145639b9004dSPhilipp Zabel if (IS_ERR(ipu->clk)) { 145739b9004dSPhilipp Zabel ret = PTR_ERR(ipu->clk); 145839b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_get failed with %d", ret); 145939b9004dSPhilipp Zabel return ret; 146039b9004dSPhilipp Zabel } 146139b9004dSPhilipp Zabel 146239b9004dSPhilipp Zabel platform_set_drvdata(pdev, ipu); 146339b9004dSPhilipp Zabel 146439b9004dSPhilipp Zabel ret = clk_prepare_enable(ipu->clk); 146539b9004dSPhilipp Zabel if (ret) { 146639b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); 146739b9004dSPhilipp Zabel return ret; 146839b9004dSPhilipp Zabel } 146939b9004dSPhilipp Zabel 147039b9004dSPhilipp Zabel ipu->dev = &pdev->dev; 147139b9004dSPhilipp Zabel ipu->irq_sync = irq_sync; 147239b9004dSPhilipp Zabel ipu->irq_err = irq_err; 147339b9004dSPhilipp Zabel 147439b9004dSPhilipp Zabel ret = device_reset(&pdev->dev); 147539b9004dSPhilipp Zabel if (ret) { 147639b9004dSPhilipp Zabel dev_err(&pdev->dev, "failed to reset: %d\n", ret); 147739b9004dSPhilipp Zabel goto out_failed_reset; 147839b9004dSPhilipp Zabel } 147939b9004dSPhilipp Zabel ret = ipu_memory_reset(ipu); 148039b9004dSPhilipp Zabel if (ret) 148139b9004dSPhilipp Zabel goto out_failed_reset; 148239b9004dSPhilipp Zabel 1483596a65d1SDavid Jander ret = ipu_irq_init(ipu); 1484596a65d1SDavid Jander if (ret) 1485596a65d1SDavid Jander goto out_failed_irq; 1486596a65d1SDavid Jander 148739b9004dSPhilipp Zabel /* Set MCU_T to divide MCU access window into 2 */ 148839b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), 148939b9004dSPhilipp Zabel IPU_DISP_GEN); 149039b9004dSPhilipp Zabel 149139b9004dSPhilipp Zabel ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk); 149239b9004dSPhilipp Zabel if (ret) 149339b9004dSPhilipp Zabel goto failed_submodules_init; 149439b9004dSPhilipp Zabel 1495d6ca8ca7SPhilipp Zabel ret = ipu_add_client_devices(ipu, ipu_base); 149639b9004dSPhilipp Zabel if (ret) { 149739b9004dSPhilipp Zabel dev_err(&pdev->dev, "adding client devices failed with %d\n", 149839b9004dSPhilipp Zabel ret); 149939b9004dSPhilipp Zabel goto failed_add_clients; 150039b9004dSPhilipp Zabel } 150139b9004dSPhilipp Zabel 150239b9004dSPhilipp Zabel dev_info(&pdev->dev, "%s probed\n", devtype->name); 150339b9004dSPhilipp Zabel 150439b9004dSPhilipp Zabel return 0; 150539b9004dSPhilipp Zabel 150639b9004dSPhilipp Zabel failed_add_clients: 150739b9004dSPhilipp Zabel ipu_submodules_exit(ipu); 150839b9004dSPhilipp Zabel failed_submodules_init: 150939b9004dSPhilipp Zabel ipu_irq_exit(ipu); 151039b9004dSPhilipp Zabel out_failed_irq: 1511596a65d1SDavid Jander out_failed_reset: 151239b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk); 151339b9004dSPhilipp Zabel return ret; 151439b9004dSPhilipp Zabel } 151539b9004dSPhilipp Zabel 151639b9004dSPhilipp Zabel static int ipu_remove(struct platform_device *pdev) 151739b9004dSPhilipp Zabel { 151839b9004dSPhilipp Zabel struct ipu_soc *ipu = platform_get_drvdata(pdev); 151939b9004dSPhilipp Zabel 152039b9004dSPhilipp Zabel platform_device_unregister_children(pdev); 152139b9004dSPhilipp Zabel ipu_submodules_exit(ipu); 152239b9004dSPhilipp Zabel ipu_irq_exit(ipu); 152339b9004dSPhilipp Zabel 152439b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk); 152539b9004dSPhilipp Zabel 152639b9004dSPhilipp Zabel return 0; 152739b9004dSPhilipp Zabel } 152839b9004dSPhilipp Zabel 152939b9004dSPhilipp Zabel static struct platform_driver imx_ipu_driver = { 153039b9004dSPhilipp Zabel .driver = { 153139b9004dSPhilipp Zabel .name = "imx-ipuv3", 153239b9004dSPhilipp Zabel .of_match_table = imx_ipu_dt_ids, 153339b9004dSPhilipp Zabel }, 153439b9004dSPhilipp Zabel .probe = ipu_probe, 153539b9004dSPhilipp Zabel .remove = ipu_remove, 153639b9004dSPhilipp Zabel }; 153739b9004dSPhilipp Zabel 1538d2a34232SLucas Stach static struct platform_driver * const drivers[] = { 153930310c83SLucas Stach #if IS_ENABLED(CONFIG_DRM) 1540d2a34232SLucas Stach &ipu_pre_drv, 1541ea9c2605SLucas Stach &ipu_prg_drv, 154230310c83SLucas Stach #endif 1543d2a34232SLucas Stach &imx_ipu_driver, 1544d2a34232SLucas Stach }; 1545d2a34232SLucas Stach 1546d2a34232SLucas Stach static int __init imx_ipu_init(void) 1547d2a34232SLucas Stach { 1548d2a34232SLucas Stach return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); 1549d2a34232SLucas Stach } 1550d2a34232SLucas Stach module_init(imx_ipu_init); 1551d2a34232SLucas Stach 1552d2a34232SLucas Stach static void __exit imx_ipu_exit(void) 1553d2a34232SLucas Stach { 1554d2a34232SLucas Stach platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); 1555d2a34232SLucas Stach } 1556d2a34232SLucas Stach module_exit(imx_ipu_exit); 155739b9004dSPhilipp Zabel 155839b9004dSPhilipp Zabel MODULE_ALIAS("platform:imx-ipuv3"); 155939b9004dSPhilipp Zabel MODULE_DESCRIPTION("i.MX IPU v3 driver"); 156039b9004dSPhilipp Zabel MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 156139b9004dSPhilipp Zabel MODULE_LICENSE("GPL"); 1562