139b9004dSPhilipp Zabel /* 239b9004dSPhilipp Zabel * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> 339b9004dSPhilipp Zabel * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. 439b9004dSPhilipp Zabel * 539b9004dSPhilipp Zabel * This program is free software; you can redistribute it and/or modify it 639b9004dSPhilipp Zabel * under the terms of the GNU General Public License as published by the 739b9004dSPhilipp Zabel * Free Software Foundation; either version 2 of the License, or (at your 839b9004dSPhilipp Zabel * option) any later version. 939b9004dSPhilipp Zabel * 1039b9004dSPhilipp Zabel * This program is distributed in the hope that it will be useful, but 1139b9004dSPhilipp Zabel * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 1239b9004dSPhilipp Zabel * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1339b9004dSPhilipp Zabel * for more details. 1439b9004dSPhilipp Zabel */ 1539b9004dSPhilipp Zabel #include <linux/module.h> 1639b9004dSPhilipp Zabel #include <linux/export.h> 1739b9004dSPhilipp Zabel #include <linux/types.h> 1839b9004dSPhilipp Zabel #include <linux/reset.h> 1939b9004dSPhilipp Zabel #include <linux/platform_device.h> 2039b9004dSPhilipp Zabel #include <linux/err.h> 2139b9004dSPhilipp Zabel #include <linux/spinlock.h> 2239b9004dSPhilipp Zabel #include <linux/delay.h> 2339b9004dSPhilipp Zabel #include <linux/interrupt.h> 2439b9004dSPhilipp Zabel #include <linux/io.h> 2539b9004dSPhilipp Zabel #include <linux/clk.h> 2639b9004dSPhilipp Zabel #include <linux/list.h> 2739b9004dSPhilipp Zabel #include <linux/irq.h> 2839b9004dSPhilipp Zabel #include <linux/irqchip/chained_irq.h> 2939b9004dSPhilipp Zabel #include <linux/irqdomain.h> 3039b9004dSPhilipp Zabel #include <linux/of_device.h> 31304e6be6SPhilipp Zabel #include <linux/of_graph.h> 3239b9004dSPhilipp Zabel 3339b9004dSPhilipp Zabel #include <drm/drm_fourcc.h> 3439b9004dSPhilipp Zabel 3539b9004dSPhilipp Zabel #include <video/imx-ipu-v3.h> 3639b9004dSPhilipp Zabel #include "ipu-prv.h" 3739b9004dSPhilipp Zabel 3839b9004dSPhilipp Zabel static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset) 3939b9004dSPhilipp Zabel { 4039b9004dSPhilipp Zabel return readl(ipu->cm_reg + offset); 4139b9004dSPhilipp Zabel } 4239b9004dSPhilipp Zabel 4339b9004dSPhilipp Zabel static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset) 4439b9004dSPhilipp Zabel { 4539b9004dSPhilipp Zabel writel(value, ipu->cm_reg + offset); 4639b9004dSPhilipp Zabel } 4739b9004dSPhilipp Zabel 48572a7615SSteve Longerbeam int ipu_get_num(struct ipu_soc *ipu) 49572a7615SSteve Longerbeam { 50572a7615SSteve Longerbeam return ipu->id; 51572a7615SSteve Longerbeam } 52572a7615SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_get_num); 53572a7615SSteve Longerbeam 5439b9004dSPhilipp Zabel void ipu_srm_dp_sync_update(struct ipu_soc *ipu) 5539b9004dSPhilipp Zabel { 5639b9004dSPhilipp Zabel u32 val; 5739b9004dSPhilipp Zabel 5839b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_SRM_PRI2); 5939b9004dSPhilipp Zabel val |= 0x8; 6039b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_SRM_PRI2); 6139b9004dSPhilipp Zabel } 6239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_srm_dp_sync_update); 6339b9004dSPhilipp Zabel 6439b9004dSPhilipp Zabel enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) 6539b9004dSPhilipp Zabel { 6639b9004dSPhilipp Zabel switch (drm_fourcc) { 670cb8b757SPhilipp Zabel case DRM_FORMAT_ARGB1555: 680cb8b757SPhilipp Zabel case DRM_FORMAT_ABGR1555: 690cb8b757SPhilipp Zabel case DRM_FORMAT_RGBA5551: 700cb8b757SPhilipp Zabel case DRM_FORMAT_BGRA5551: 7139b9004dSPhilipp Zabel case DRM_FORMAT_RGB565: 7239b9004dSPhilipp Zabel case DRM_FORMAT_BGR565: 7339b9004dSPhilipp Zabel case DRM_FORMAT_RGB888: 7439b9004dSPhilipp Zabel case DRM_FORMAT_BGR888: 757d2e8a20SLucas Stach case DRM_FORMAT_ARGB4444: 7639b9004dSPhilipp Zabel case DRM_FORMAT_XRGB8888: 7739b9004dSPhilipp Zabel case DRM_FORMAT_XBGR8888: 7839b9004dSPhilipp Zabel case DRM_FORMAT_RGBX8888: 7939b9004dSPhilipp Zabel case DRM_FORMAT_BGRX8888: 8039b9004dSPhilipp Zabel case DRM_FORMAT_ARGB8888: 8139b9004dSPhilipp Zabel case DRM_FORMAT_ABGR8888: 8239b9004dSPhilipp Zabel case DRM_FORMAT_RGBA8888: 8339b9004dSPhilipp Zabel case DRM_FORMAT_BGRA8888: 8439b9004dSPhilipp Zabel return IPUV3_COLORSPACE_RGB; 8539b9004dSPhilipp Zabel case DRM_FORMAT_YUYV: 8639b9004dSPhilipp Zabel case DRM_FORMAT_UYVY: 8739b9004dSPhilipp Zabel case DRM_FORMAT_YUV420: 8839b9004dSPhilipp Zabel case DRM_FORMAT_YVU420: 899a34cef0SSteve Longerbeam case DRM_FORMAT_YUV422: 909a34cef0SSteve Longerbeam case DRM_FORMAT_YVU422: 91c9d508c2SPhilipp Zabel case DRM_FORMAT_YUV444: 92c9d508c2SPhilipp Zabel case DRM_FORMAT_YVU444: 939a34cef0SSteve Longerbeam case DRM_FORMAT_NV12: 949a34cef0SSteve Longerbeam case DRM_FORMAT_NV21: 959a34cef0SSteve Longerbeam case DRM_FORMAT_NV16: 969a34cef0SSteve Longerbeam case DRM_FORMAT_NV61: 9739b9004dSPhilipp Zabel return IPUV3_COLORSPACE_YUV; 9839b9004dSPhilipp Zabel default: 9939b9004dSPhilipp Zabel return IPUV3_COLORSPACE_UNKNOWN; 10039b9004dSPhilipp Zabel } 10139b9004dSPhilipp Zabel } 10239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace); 10339b9004dSPhilipp Zabel 10439b9004dSPhilipp Zabel enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) 10539b9004dSPhilipp Zabel { 10639b9004dSPhilipp Zabel switch (pixelformat) { 10739b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUV420: 10839b9004dSPhilipp Zabel case V4L2_PIX_FMT_YVU420: 1099a34cef0SSteve Longerbeam case V4L2_PIX_FMT_YUV422P: 11039b9004dSPhilipp Zabel case V4L2_PIX_FMT_UYVY: 11139b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUYV: 1129a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV12: 1139a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV21: 1149a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV16: 1159a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV61: 11639b9004dSPhilipp Zabel return IPUV3_COLORSPACE_YUV; 11739b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB32: 11839b9004dSPhilipp Zabel case V4L2_PIX_FMT_BGR32: 11939b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB24: 12039b9004dSPhilipp Zabel case V4L2_PIX_FMT_BGR24: 12139b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB565: 12239b9004dSPhilipp Zabel return IPUV3_COLORSPACE_RGB; 12339b9004dSPhilipp Zabel default: 12439b9004dSPhilipp Zabel return IPUV3_COLORSPACE_UNKNOWN; 12539b9004dSPhilipp Zabel } 12639b9004dSPhilipp Zabel } 12739b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace); 12839b9004dSPhilipp Zabel 1294cea940dSSteve Longerbeam bool ipu_pixelformat_is_planar(u32 pixelformat) 1304cea940dSSteve Longerbeam { 1314cea940dSSteve Longerbeam switch (pixelformat) { 1324cea940dSSteve Longerbeam case V4L2_PIX_FMT_YUV420: 1334cea940dSSteve Longerbeam case V4L2_PIX_FMT_YVU420: 1349a34cef0SSteve Longerbeam case V4L2_PIX_FMT_YUV422P: 1359a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV12: 1369a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV21: 1379a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV16: 1389a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV61: 1394cea940dSSteve Longerbeam return true; 1404cea940dSSteve Longerbeam } 1414cea940dSSteve Longerbeam 1424cea940dSSteve Longerbeam return false; 1434cea940dSSteve Longerbeam } 1444cea940dSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar); 1454cea940dSSteve Longerbeam 146ae0e9708SSteve Longerbeam enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code) 147ae0e9708SSteve Longerbeam { 148ae0e9708SSteve Longerbeam switch (mbus_code & 0xf000) { 149ae0e9708SSteve Longerbeam case 0x1000: 150ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_RGB; 151ae0e9708SSteve Longerbeam case 0x2000: 152ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_YUV; 153ae0e9708SSteve Longerbeam default: 154ae0e9708SSteve Longerbeam return IPUV3_COLORSPACE_UNKNOWN; 155ae0e9708SSteve Longerbeam } 156ae0e9708SSteve Longerbeam } 157ae0e9708SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace); 158ae0e9708SSteve Longerbeam 1596930afdcSSteve Longerbeam int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat) 1606930afdcSSteve Longerbeam { 1616930afdcSSteve Longerbeam switch (pixelformat) { 1626930afdcSSteve Longerbeam case V4L2_PIX_FMT_YUV420: 1636930afdcSSteve Longerbeam case V4L2_PIX_FMT_YVU420: 1649a34cef0SSteve Longerbeam case V4L2_PIX_FMT_YUV422P: 1659a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV12: 1669a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV21: 1679a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV16: 1689a34cef0SSteve Longerbeam case V4L2_PIX_FMT_NV61: 1696930afdcSSteve Longerbeam /* 1706930afdcSSteve Longerbeam * for the planar YUV formats, the stride passed to 1716930afdcSSteve Longerbeam * cpmem must be the stride in bytes of the Y plane. 1726930afdcSSteve Longerbeam * And all the planar YUV formats have an 8-bit 1736930afdcSSteve Longerbeam * Y component. 1746930afdcSSteve Longerbeam */ 1756930afdcSSteve Longerbeam return (8 * pixel_stride) >> 3; 1766930afdcSSteve Longerbeam case V4L2_PIX_FMT_RGB565: 1776930afdcSSteve Longerbeam case V4L2_PIX_FMT_YUYV: 1786930afdcSSteve Longerbeam case V4L2_PIX_FMT_UYVY: 1796930afdcSSteve Longerbeam return (16 * pixel_stride) >> 3; 1806930afdcSSteve Longerbeam case V4L2_PIX_FMT_BGR24: 1816930afdcSSteve Longerbeam case V4L2_PIX_FMT_RGB24: 1826930afdcSSteve Longerbeam return (24 * pixel_stride) >> 3; 1836930afdcSSteve Longerbeam case V4L2_PIX_FMT_BGR32: 1846930afdcSSteve Longerbeam case V4L2_PIX_FMT_RGB32: 1856930afdcSSteve Longerbeam return (32 * pixel_stride) >> 3; 1866930afdcSSteve Longerbeam default: 1876930afdcSSteve Longerbeam break; 1886930afdcSSteve Longerbeam } 1896930afdcSSteve Longerbeam 1906930afdcSSteve Longerbeam return -EINVAL; 1916930afdcSSteve Longerbeam } 1926930afdcSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_stride_to_bytes); 1936930afdcSSteve Longerbeam 194f835f386SSteve Longerbeam int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees, 195f835f386SSteve Longerbeam bool hflip, bool vflip) 196f835f386SSteve Longerbeam { 197f835f386SSteve Longerbeam u32 r90, vf, hf; 198f835f386SSteve Longerbeam 199f835f386SSteve Longerbeam switch (degrees) { 200f835f386SSteve Longerbeam case 0: 201f835f386SSteve Longerbeam vf = hf = r90 = 0; 202f835f386SSteve Longerbeam break; 203f835f386SSteve Longerbeam case 90: 204f835f386SSteve Longerbeam vf = hf = 0; 205f835f386SSteve Longerbeam r90 = 1; 206f835f386SSteve Longerbeam break; 207f835f386SSteve Longerbeam case 180: 208f835f386SSteve Longerbeam vf = hf = 1; 209f835f386SSteve Longerbeam r90 = 0; 210f835f386SSteve Longerbeam break; 211f835f386SSteve Longerbeam case 270: 212f835f386SSteve Longerbeam vf = hf = r90 = 1; 213f835f386SSteve Longerbeam break; 214f835f386SSteve Longerbeam default: 215f835f386SSteve Longerbeam return -EINVAL; 216f835f386SSteve Longerbeam } 217f835f386SSteve Longerbeam 218f835f386SSteve Longerbeam hf ^= (u32)hflip; 219f835f386SSteve Longerbeam vf ^= (u32)vflip; 220f835f386SSteve Longerbeam 221f835f386SSteve Longerbeam *mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf); 222f835f386SSteve Longerbeam return 0; 223f835f386SSteve Longerbeam } 224f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode); 225f835f386SSteve Longerbeam 226f835f386SSteve Longerbeam int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode, 227f835f386SSteve Longerbeam bool hflip, bool vflip) 228f835f386SSteve Longerbeam { 229f835f386SSteve Longerbeam u32 r90, vf, hf; 230f835f386SSteve Longerbeam 231f835f386SSteve Longerbeam r90 = ((u32)mode >> 2) & 0x1; 232f835f386SSteve Longerbeam hf = ((u32)mode >> 1) & 0x1; 233f835f386SSteve Longerbeam vf = ((u32)mode >> 0) & 0x1; 234f835f386SSteve Longerbeam hf ^= (u32)hflip; 235f835f386SSteve Longerbeam vf ^= (u32)vflip; 236f835f386SSteve Longerbeam 237f835f386SSteve Longerbeam switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) { 238f835f386SSteve Longerbeam case IPU_ROTATE_NONE: 239f835f386SSteve Longerbeam *degrees = 0; 240f835f386SSteve Longerbeam break; 241f835f386SSteve Longerbeam case IPU_ROTATE_90_RIGHT: 242f835f386SSteve Longerbeam *degrees = 90; 243f835f386SSteve Longerbeam break; 244f835f386SSteve Longerbeam case IPU_ROTATE_180: 245f835f386SSteve Longerbeam *degrees = 180; 246f835f386SSteve Longerbeam break; 247f835f386SSteve Longerbeam case IPU_ROTATE_90_LEFT: 248f835f386SSteve Longerbeam *degrees = 270; 249f835f386SSteve Longerbeam break; 250f835f386SSteve Longerbeam default: 251f835f386SSteve Longerbeam return -EINVAL; 252f835f386SSteve Longerbeam } 253f835f386SSteve Longerbeam 254f835f386SSteve Longerbeam return 0; 255f835f386SSteve Longerbeam } 256f835f386SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees); 257f835f386SSteve Longerbeam 25839b9004dSPhilipp Zabel struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) 25939b9004dSPhilipp Zabel { 26039b9004dSPhilipp Zabel struct ipuv3_channel *channel; 26139b9004dSPhilipp Zabel 26239b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, num); 26339b9004dSPhilipp Zabel 26439b9004dSPhilipp Zabel if (num > 63) 26539b9004dSPhilipp Zabel return ERR_PTR(-ENODEV); 26639b9004dSPhilipp Zabel 26739b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock); 26839b9004dSPhilipp Zabel 26939b9004dSPhilipp Zabel channel = &ipu->channel[num]; 27039b9004dSPhilipp Zabel 27139b9004dSPhilipp Zabel if (channel->busy) { 27239b9004dSPhilipp Zabel channel = ERR_PTR(-EBUSY); 27339b9004dSPhilipp Zabel goto out; 27439b9004dSPhilipp Zabel } 27539b9004dSPhilipp Zabel 27639b9004dSPhilipp Zabel channel->busy = true; 27739b9004dSPhilipp Zabel channel->num = num; 27839b9004dSPhilipp Zabel 27939b9004dSPhilipp Zabel out: 28039b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock); 28139b9004dSPhilipp Zabel 28239b9004dSPhilipp Zabel return channel; 28339b9004dSPhilipp Zabel } 28439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get); 28539b9004dSPhilipp Zabel 28639b9004dSPhilipp Zabel void ipu_idmac_put(struct ipuv3_channel *channel) 28739b9004dSPhilipp Zabel { 28839b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 28939b9004dSPhilipp Zabel 29039b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num); 29139b9004dSPhilipp Zabel 29239b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock); 29339b9004dSPhilipp Zabel 29439b9004dSPhilipp Zabel channel->busy = false; 29539b9004dSPhilipp Zabel 29639b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock); 29739b9004dSPhilipp Zabel } 29839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_put); 29939b9004dSPhilipp Zabel 300aa52f578SSteve Longerbeam #define idma_mask(ch) (1 << ((ch) & 0x1f)) 30139b9004dSPhilipp Zabel 302e7268c69SSteve Longerbeam /* 303e7268c69SSteve Longerbeam * This is an undocumented feature, a write one to a channel bit in 304e7268c69SSteve Longerbeam * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's 305e7268c69SSteve Longerbeam * internal current buffer pointer so that transfers start from buffer 306e7268c69SSteve Longerbeam * 0 on the next channel enable (that's the theory anyway, the imx6 TRM 307e7268c69SSteve Longerbeam * only says these are read-only registers). This operation is required 308e7268c69SSteve Longerbeam * for channel linking to work correctly, for instance video capture 309e7268c69SSteve Longerbeam * pipelines that carry out image rotations will fail after the first 310e7268c69SSteve Longerbeam * streaming unless this function is called for each channel before 311e7268c69SSteve Longerbeam * re-enabling the channels. 312e7268c69SSteve Longerbeam */ 313e7268c69SSteve Longerbeam static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel) 314e7268c69SSteve Longerbeam { 315e7268c69SSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 316e7268c69SSteve Longerbeam unsigned int chno = channel->num; 317e7268c69SSteve Longerbeam 318e7268c69SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno)); 319e7268c69SSteve Longerbeam } 320e7268c69SSteve Longerbeam 32139b9004dSPhilipp Zabel void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, 32239b9004dSPhilipp Zabel bool doublebuffer) 32339b9004dSPhilipp Zabel { 32439b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 32539b9004dSPhilipp Zabel unsigned long flags; 32639b9004dSPhilipp Zabel u32 reg; 32739b9004dSPhilipp Zabel 32839b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 32939b9004dSPhilipp Zabel 33039b9004dSPhilipp Zabel reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 33139b9004dSPhilipp Zabel if (doublebuffer) 33239b9004dSPhilipp Zabel reg |= idma_mask(channel->num); 33339b9004dSPhilipp Zabel else 33439b9004dSPhilipp Zabel reg &= ~idma_mask(channel->num); 33539b9004dSPhilipp Zabel ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num)); 33639b9004dSPhilipp Zabel 337e7268c69SSteve Longerbeam __ipu_idmac_reset_current_buffer(channel); 338e7268c69SSteve Longerbeam 33939b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 34039b9004dSPhilipp Zabel } 34139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer); 34239b9004dSPhilipp Zabel 3434fd1a07aSSteve Longerbeam static const struct { 3444fd1a07aSSteve Longerbeam int chnum; 3454fd1a07aSSteve Longerbeam u32 reg; 3464fd1a07aSSteve Longerbeam int shift; 3474fd1a07aSSteve Longerbeam } idmac_lock_en_info[] = { 3484fd1a07aSSteve Longerbeam { .chnum = 5, .reg = IDMAC_CH_LOCK_EN_1, .shift = 0, }, 3494fd1a07aSSteve Longerbeam { .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift = 2, }, 3504fd1a07aSSteve Longerbeam { .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift = 4, }, 3514fd1a07aSSteve Longerbeam { .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift = 6, }, 3524fd1a07aSSteve Longerbeam { .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift = 8, }, 3534fd1a07aSSteve Longerbeam { .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, }, 3544fd1a07aSSteve Longerbeam { .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, }, 3554fd1a07aSSteve Longerbeam { .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, }, 3564fd1a07aSSteve Longerbeam { .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, }, 3574fd1a07aSSteve Longerbeam { .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, }, 3584fd1a07aSSteve Longerbeam { .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, }, 3594fd1a07aSSteve Longerbeam { .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift = 0, }, 3604fd1a07aSSteve Longerbeam { .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift = 2, }, 3614fd1a07aSSteve Longerbeam { .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift = 4, }, 3624fd1a07aSSteve Longerbeam { .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift = 6, }, 3634fd1a07aSSteve Longerbeam { .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift = 8, }, 3644fd1a07aSSteve Longerbeam { .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, }, 3654fd1a07aSSteve Longerbeam }; 3664fd1a07aSSteve Longerbeam 3674fd1a07aSSteve Longerbeam int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts) 3684fd1a07aSSteve Longerbeam { 3694fd1a07aSSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 3704fd1a07aSSteve Longerbeam unsigned long flags; 3714fd1a07aSSteve Longerbeam u32 bursts, regval; 3724fd1a07aSSteve Longerbeam int i; 3734fd1a07aSSteve Longerbeam 3744fd1a07aSSteve Longerbeam switch (num_bursts) { 3754fd1a07aSSteve Longerbeam case 0: 3764fd1a07aSSteve Longerbeam case 1: 3774fd1a07aSSteve Longerbeam bursts = 0x00; /* locking disabled */ 3784fd1a07aSSteve Longerbeam break; 3794fd1a07aSSteve Longerbeam case 2: 3804fd1a07aSSteve Longerbeam bursts = 0x01; 3814fd1a07aSSteve Longerbeam break; 3824fd1a07aSSteve Longerbeam case 4: 3834fd1a07aSSteve Longerbeam bursts = 0x02; 3844fd1a07aSSteve Longerbeam break; 3854fd1a07aSSteve Longerbeam case 8: 3864fd1a07aSSteve Longerbeam bursts = 0x03; 3874fd1a07aSSteve Longerbeam break; 3884fd1a07aSSteve Longerbeam default: 3894fd1a07aSSteve Longerbeam return -EINVAL; 3904fd1a07aSSteve Longerbeam } 3914fd1a07aSSteve Longerbeam 3924fd1a07aSSteve Longerbeam for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) { 3934fd1a07aSSteve Longerbeam if (channel->num == idmac_lock_en_info[i].chnum) 3944fd1a07aSSteve Longerbeam break; 3954fd1a07aSSteve Longerbeam } 3964fd1a07aSSteve Longerbeam if (i >= ARRAY_SIZE(idmac_lock_en_info)) 3974fd1a07aSSteve Longerbeam return -EINVAL; 3984fd1a07aSSteve Longerbeam 3994fd1a07aSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 4004fd1a07aSSteve Longerbeam 4014fd1a07aSSteve Longerbeam regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg); 4024fd1a07aSSteve Longerbeam regval &= ~(0x03 << idmac_lock_en_info[i].shift); 4034fd1a07aSSteve Longerbeam regval |= (bursts << idmac_lock_en_info[i].shift); 4044fd1a07aSSteve Longerbeam ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg); 4054fd1a07aSSteve Longerbeam 4064fd1a07aSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 4074fd1a07aSSteve Longerbeam 4084fd1a07aSSteve Longerbeam return 0; 4094fd1a07aSSteve Longerbeam } 4104fd1a07aSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable); 4114fd1a07aSSteve Longerbeam 41239b9004dSPhilipp Zabel int ipu_module_enable(struct ipu_soc *ipu, u32 mask) 41339b9004dSPhilipp Zabel { 41439b9004dSPhilipp Zabel unsigned long lock_flags; 41539b9004dSPhilipp Zabel u32 val; 41639b9004dSPhilipp Zabel 41739b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags); 41839b9004dSPhilipp Zabel 41939b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN); 42039b9004dSPhilipp Zabel 42139b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN) 42239b9004dSPhilipp Zabel val |= IPU_DI0_COUNTER_RELEASE; 42339b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN) 42439b9004dSPhilipp Zabel val |= IPU_DI1_COUNTER_RELEASE; 42539b9004dSPhilipp Zabel 42639b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN); 42739b9004dSPhilipp Zabel 42839b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF); 42939b9004dSPhilipp Zabel val |= mask; 43039b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF); 43139b9004dSPhilipp Zabel 43239b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags); 43339b9004dSPhilipp Zabel 43439b9004dSPhilipp Zabel return 0; 43539b9004dSPhilipp Zabel } 43639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_enable); 43739b9004dSPhilipp Zabel 43839b9004dSPhilipp Zabel int ipu_module_disable(struct ipu_soc *ipu, u32 mask) 43939b9004dSPhilipp Zabel { 44039b9004dSPhilipp Zabel unsigned long lock_flags; 44139b9004dSPhilipp Zabel u32 val; 44239b9004dSPhilipp Zabel 44339b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags); 44439b9004dSPhilipp Zabel 44539b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF); 44639b9004dSPhilipp Zabel val &= ~mask; 44739b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF); 44839b9004dSPhilipp Zabel 44939b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN); 45039b9004dSPhilipp Zabel 45139b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN) 45239b9004dSPhilipp Zabel val &= ~IPU_DI0_COUNTER_RELEASE; 45339b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN) 45439b9004dSPhilipp Zabel val &= ~IPU_DI1_COUNTER_RELEASE; 45539b9004dSPhilipp Zabel 45639b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN); 45739b9004dSPhilipp Zabel 45839b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags); 45939b9004dSPhilipp Zabel 46039b9004dSPhilipp Zabel return 0; 46139b9004dSPhilipp Zabel } 46239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_disable); 46339b9004dSPhilipp Zabel 464e9046097SPhilipp Zabel int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel) 465e9046097SPhilipp Zabel { 466e9046097SPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 467e9046097SPhilipp Zabel unsigned int chno = channel->num; 468e9046097SPhilipp Zabel 469e9046097SPhilipp Zabel return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0; 470e9046097SPhilipp Zabel } 471e9046097SPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer); 472e9046097SPhilipp Zabel 473aa52f578SSteve Longerbeam bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num) 474aa52f578SSteve Longerbeam { 475aa52f578SSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 476aa52f578SSteve Longerbeam unsigned long flags; 477aa52f578SSteve Longerbeam u32 reg = 0; 478aa52f578SSteve Longerbeam 479aa52f578SSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 480aa52f578SSteve Longerbeam switch (buf_num) { 481aa52f578SSteve Longerbeam case 0: 482aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)); 483aa52f578SSteve Longerbeam break; 484aa52f578SSteve Longerbeam case 1: 485aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)); 486aa52f578SSteve Longerbeam break; 487aa52f578SSteve Longerbeam case 2: 488aa52f578SSteve Longerbeam reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num)); 489aa52f578SSteve Longerbeam break; 490aa52f578SSteve Longerbeam } 491aa52f578SSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 492aa52f578SSteve Longerbeam 493aa52f578SSteve Longerbeam return ((reg & idma_mask(channel->num)) != 0); 494aa52f578SSteve Longerbeam } 495aa52f578SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready); 496aa52f578SSteve Longerbeam 49739b9004dSPhilipp Zabel void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num) 49839b9004dSPhilipp Zabel { 49939b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 50039b9004dSPhilipp Zabel unsigned int chno = channel->num; 50139b9004dSPhilipp Zabel unsigned long flags; 50239b9004dSPhilipp Zabel 50339b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 50439b9004dSPhilipp Zabel 50539b9004dSPhilipp Zabel /* Mark buffer as ready. */ 50639b9004dSPhilipp Zabel if (buf_num == 0) 50739b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); 50839b9004dSPhilipp Zabel else 50939b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); 51039b9004dSPhilipp Zabel 51139b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 51239b9004dSPhilipp Zabel } 51339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer); 51439b9004dSPhilipp Zabel 515bce6f087SSteve Longerbeam void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num) 516bce6f087SSteve Longerbeam { 517bce6f087SSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 518bce6f087SSteve Longerbeam unsigned int chno = channel->num; 519bce6f087SSteve Longerbeam unsigned long flags; 520bce6f087SSteve Longerbeam 521bce6f087SSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 522bce6f087SSteve Longerbeam 523bce6f087SSteve Longerbeam ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */ 524bce6f087SSteve Longerbeam switch (buf_num) { 525bce6f087SSteve Longerbeam case 0: 526bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); 527bce6f087SSteve Longerbeam break; 528bce6f087SSteve Longerbeam case 1: 529bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); 530bce6f087SSteve Longerbeam break; 531bce6f087SSteve Longerbeam case 2: 532bce6f087SSteve Longerbeam ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno)); 533bce6f087SSteve Longerbeam break; 534bce6f087SSteve Longerbeam default: 535bce6f087SSteve Longerbeam break; 536bce6f087SSteve Longerbeam } 537bce6f087SSteve Longerbeam ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ 538bce6f087SSteve Longerbeam 539bce6f087SSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 540bce6f087SSteve Longerbeam } 541bce6f087SSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer); 542bce6f087SSteve Longerbeam 54339b9004dSPhilipp Zabel int ipu_idmac_enable_channel(struct ipuv3_channel *channel) 54439b9004dSPhilipp Zabel { 54539b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 54639b9004dSPhilipp Zabel u32 val; 54739b9004dSPhilipp Zabel unsigned long flags; 54839b9004dSPhilipp Zabel 54939b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 55039b9004dSPhilipp Zabel 55139b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 55239b9004dSPhilipp Zabel val |= idma_mask(channel->num); 55339b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 55439b9004dSPhilipp Zabel 55539b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 55639b9004dSPhilipp Zabel 55739b9004dSPhilipp Zabel return 0; 55839b9004dSPhilipp Zabel } 55939b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); 56039b9004dSPhilipp Zabel 561682b7c1cSLinus Torvalds bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) 562682b7c1cSLinus Torvalds { 563682b7c1cSLinus Torvalds return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); 564682b7c1cSLinus Torvalds } 565682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); 566682b7c1cSLinus Torvalds 56739b9004dSPhilipp Zabel int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) 56839b9004dSPhilipp Zabel { 56939b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 57039b9004dSPhilipp Zabel unsigned long timeout; 57139b9004dSPhilipp Zabel 57239b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(ms); 57339b9004dSPhilipp Zabel while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) & 57439b9004dSPhilipp Zabel idma_mask(channel->num)) { 57539b9004dSPhilipp Zabel if (time_after(jiffies, timeout)) 57639b9004dSPhilipp Zabel return -ETIMEDOUT; 57739b9004dSPhilipp Zabel cpu_relax(); 57839b9004dSPhilipp Zabel } 57939b9004dSPhilipp Zabel 58039b9004dSPhilipp Zabel return 0; 58139b9004dSPhilipp Zabel } 58239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); 58339b9004dSPhilipp Zabel 584682b7c1cSLinus Torvalds int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms) 585682b7c1cSLinus Torvalds { 586682b7c1cSLinus Torvalds unsigned long timeout; 587682b7c1cSLinus Torvalds 588682b7c1cSLinus Torvalds timeout = jiffies + msecs_to_jiffies(ms); 589682b7c1cSLinus Torvalds ipu_cm_write(ipu, BIT(irq % 32), IPU_INT_STAT(irq / 32)); 590682b7c1cSLinus Torvalds while (!(ipu_cm_read(ipu, IPU_INT_STAT(irq / 32) & BIT(irq % 32)))) { 591682b7c1cSLinus Torvalds if (time_after(jiffies, timeout)) 592682b7c1cSLinus Torvalds return -ETIMEDOUT; 593682b7c1cSLinus Torvalds cpu_relax(); 594682b7c1cSLinus Torvalds } 595682b7c1cSLinus Torvalds 596682b7c1cSLinus Torvalds return 0; 597682b7c1cSLinus Torvalds } 598682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_wait_interrupt); 599682b7c1cSLinus Torvalds 60039b9004dSPhilipp Zabel int ipu_idmac_disable_channel(struct ipuv3_channel *channel) 60139b9004dSPhilipp Zabel { 60239b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 60339b9004dSPhilipp Zabel u32 val; 60439b9004dSPhilipp Zabel unsigned long flags; 60539b9004dSPhilipp Zabel 60639b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 60739b9004dSPhilipp Zabel 60839b9004dSPhilipp Zabel /* Disable DMA channel(s) */ 60939b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 61039b9004dSPhilipp Zabel val &= ~idma_mask(channel->num); 61139b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 61239b9004dSPhilipp Zabel 613e7268c69SSteve Longerbeam __ipu_idmac_reset_current_buffer(channel); 614e7268c69SSteve Longerbeam 61539b9004dSPhilipp Zabel /* Set channel buffers NOT to be ready */ 61639b9004dSPhilipp Zabel ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */ 61739b9004dSPhilipp Zabel 61839b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) & 61939b9004dSPhilipp Zabel idma_mask(channel->num)) { 62039b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num), 62139b9004dSPhilipp Zabel IPU_CHA_BUF0_RDY(channel->num)); 62239b9004dSPhilipp Zabel } 62339b9004dSPhilipp Zabel 62439b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) & 62539b9004dSPhilipp Zabel idma_mask(channel->num)) { 62639b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num), 62739b9004dSPhilipp Zabel IPU_CHA_BUF1_RDY(channel->num)); 62839b9004dSPhilipp Zabel } 62939b9004dSPhilipp Zabel 63039b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ 63139b9004dSPhilipp Zabel 63239b9004dSPhilipp Zabel /* Reset the double buffer */ 63339b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 63439b9004dSPhilipp Zabel val &= ~idma_mask(channel->num); 63539b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num)); 63639b9004dSPhilipp Zabel 63739b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 63839b9004dSPhilipp Zabel 63939b9004dSPhilipp Zabel return 0; 64039b9004dSPhilipp Zabel } 64139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel); 64239b9004dSPhilipp Zabel 6432bcf577eSSteve Longerbeam /* 6442bcf577eSSteve Longerbeam * The imx6 rev. D TRM says that enabling the WM feature will increase 6452bcf577eSSteve Longerbeam * a channel's priority. Refer to Table 36-8 Calculated priority value. 6462bcf577eSSteve Longerbeam * The sub-module that is the sink or source for the channel must enable 6472bcf577eSSteve Longerbeam * watermark signal for this to take effect (SMFC_WM for instance). 6482bcf577eSSteve Longerbeam */ 6492bcf577eSSteve Longerbeam void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable) 6502bcf577eSSteve Longerbeam { 6512bcf577eSSteve Longerbeam struct ipu_soc *ipu = channel->ipu; 6522bcf577eSSteve Longerbeam unsigned long flags; 6532bcf577eSSteve Longerbeam u32 val; 6542bcf577eSSteve Longerbeam 6552bcf577eSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 6562bcf577eSSteve Longerbeam 6572bcf577eSSteve Longerbeam val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num)); 6582bcf577eSSteve Longerbeam if (enable) 6592bcf577eSSteve Longerbeam val |= 1 << (channel->num % 32); 6602bcf577eSSteve Longerbeam else 6612bcf577eSSteve Longerbeam val &= ~(1 << (channel->num % 32)); 6622bcf577eSSteve Longerbeam ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num)); 6632bcf577eSSteve Longerbeam 6642bcf577eSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 6652bcf577eSSteve Longerbeam } 6662bcf577eSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark); 6672bcf577eSSteve Longerbeam 66839b9004dSPhilipp Zabel static int ipu_memory_reset(struct ipu_soc *ipu) 66939b9004dSPhilipp Zabel { 67039b9004dSPhilipp Zabel unsigned long timeout; 67139b9004dSPhilipp Zabel 67239b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); 67339b9004dSPhilipp Zabel 67439b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(1000); 67539b9004dSPhilipp Zabel while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { 67639b9004dSPhilipp Zabel if (time_after(jiffies, timeout)) 67739b9004dSPhilipp Zabel return -ETIME; 67839b9004dSPhilipp Zabel cpu_relax(); 67939b9004dSPhilipp Zabel } 68039b9004dSPhilipp Zabel 68139b9004dSPhilipp Zabel return 0; 68239b9004dSPhilipp Zabel } 68339b9004dSPhilipp Zabel 684ba07975fSSteve Longerbeam /* 685ba07975fSSteve Longerbeam * Set the source mux for the given CSI. Selects either parallel or 686ba07975fSSteve Longerbeam * MIPI CSI2 sources. 687ba07975fSSteve Longerbeam */ 688ba07975fSSteve Longerbeam void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2) 689ba07975fSSteve Longerbeam { 690ba07975fSSteve Longerbeam unsigned long flags; 691ba07975fSSteve Longerbeam u32 val, mask; 692ba07975fSSteve Longerbeam 693ba07975fSSteve Longerbeam mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE : 694ba07975fSSteve Longerbeam IPU_CONF_CSI0_DATA_SOURCE; 695ba07975fSSteve Longerbeam 696ba07975fSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 697ba07975fSSteve Longerbeam 698ba07975fSSteve Longerbeam val = ipu_cm_read(ipu, IPU_CONF); 699ba07975fSSteve Longerbeam if (mipi_csi2) 700ba07975fSSteve Longerbeam val |= mask; 701ba07975fSSteve Longerbeam else 702ba07975fSSteve Longerbeam val &= ~mask; 703ba07975fSSteve Longerbeam ipu_cm_write(ipu, val, IPU_CONF); 704ba07975fSSteve Longerbeam 705ba07975fSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 706ba07975fSSteve Longerbeam } 707ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux); 708ba07975fSSteve Longerbeam 709ba07975fSSteve Longerbeam /* 710ba07975fSSteve Longerbeam * Set the source mux for the IC. Selects either CSI[01] or the VDI. 711ba07975fSSteve Longerbeam */ 712ba07975fSSteve Longerbeam void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) 713ba07975fSSteve Longerbeam { 714ba07975fSSteve Longerbeam unsigned long flags; 715ba07975fSSteve Longerbeam u32 val; 716ba07975fSSteve Longerbeam 717ba07975fSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 718ba07975fSSteve Longerbeam 719ba07975fSSteve Longerbeam val = ipu_cm_read(ipu, IPU_CONF); 720ba07975fSSteve Longerbeam if (vdi) { 721ba07975fSSteve Longerbeam val |= IPU_CONF_IC_INPUT; 722ba07975fSSteve Longerbeam } else { 723ba07975fSSteve Longerbeam val &= ~IPU_CONF_IC_INPUT; 724ba07975fSSteve Longerbeam if (csi_id == 1) 725ba07975fSSteve Longerbeam val |= IPU_CONF_CSI_SEL; 726ba07975fSSteve Longerbeam else 727ba07975fSSteve Longerbeam val &= ~IPU_CONF_CSI_SEL; 728ba07975fSSteve Longerbeam } 729ba07975fSSteve Longerbeam ipu_cm_write(ipu, val, IPU_CONF); 730ba07975fSSteve Longerbeam 731ba07975fSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 732ba07975fSSteve Longerbeam } 733ba07975fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux); 734ba07975fSSteve Longerbeam 735ac4708faSSteve Longerbeam 736ac4708faSSteve Longerbeam /* Frame Synchronization Unit Channel Linking */ 737ac4708faSSteve Longerbeam 738ac4708faSSteve Longerbeam struct fsu_link_reg_info { 739ac4708faSSteve Longerbeam int chno; 740ac4708faSSteve Longerbeam u32 reg; 741ac4708faSSteve Longerbeam u32 mask; 742ac4708faSSteve Longerbeam u32 val; 743ac4708faSSteve Longerbeam }; 744ac4708faSSteve Longerbeam 745ac4708faSSteve Longerbeam struct fsu_link_info { 746ac4708faSSteve Longerbeam struct fsu_link_reg_info src; 747ac4708faSSteve Longerbeam struct fsu_link_reg_info sink; 748ac4708faSSteve Longerbeam }; 749ac4708faSSteve Longerbeam 750ac4708faSSteve Longerbeam static const struct fsu_link_info fsu_link_info[] = { 751ac4708faSSteve Longerbeam { 752ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2, 753ac4708faSSteve Longerbeam FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC }, 754ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1, 755ac4708faSSteve Longerbeam FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC }, 756ac4708faSSteve Longerbeam }, { 757ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2, 758ac4708faSSteve Longerbeam FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF }, 759ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1, 760ac4708faSSteve Longerbeam FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF }, 761ac4708faSSteve Longerbeam }, { 762ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2, 763ac4708faSSteve Longerbeam FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP }, 764ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1, 765ac4708faSSteve Longerbeam FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP }, 766ac4708faSSteve Longerbeam }, { 767ac4708faSSteve Longerbeam .src = { IPUV3_CHANNEL_CSI_DIRECT, 0 }, 768ac4708faSSteve Longerbeam .sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1, 769ac4708faSSteve Longerbeam FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT }, 770ac4708faSSteve Longerbeam }, 771ac4708faSSteve Longerbeam }; 772ac4708faSSteve Longerbeam 773ac4708faSSteve Longerbeam static const struct fsu_link_info *find_fsu_link_info(int src, int sink) 774ac4708faSSteve Longerbeam { 775ac4708faSSteve Longerbeam int i; 776ac4708faSSteve Longerbeam 777ac4708faSSteve Longerbeam for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) { 778ac4708faSSteve Longerbeam if (src == fsu_link_info[i].src.chno && 779ac4708faSSteve Longerbeam sink == fsu_link_info[i].sink.chno) 780ac4708faSSteve Longerbeam return &fsu_link_info[i]; 781ac4708faSSteve Longerbeam } 782ac4708faSSteve Longerbeam 783ac4708faSSteve Longerbeam return NULL; 784ac4708faSSteve Longerbeam } 785ac4708faSSteve Longerbeam 786ac4708faSSteve Longerbeam /* 787ac4708faSSteve Longerbeam * Links a source channel to a sink channel in the FSU. 788ac4708faSSteve Longerbeam */ 789ac4708faSSteve Longerbeam int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch) 790ac4708faSSteve Longerbeam { 791ac4708faSSteve Longerbeam const struct fsu_link_info *link; 792ac4708faSSteve Longerbeam u32 src_reg, sink_reg; 793ac4708faSSteve Longerbeam unsigned long flags; 794ac4708faSSteve Longerbeam 795ac4708faSSteve Longerbeam link = find_fsu_link_info(src_ch, sink_ch); 796ac4708faSSteve Longerbeam if (!link) 797ac4708faSSteve Longerbeam return -EINVAL; 798ac4708faSSteve Longerbeam 799ac4708faSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 800ac4708faSSteve Longerbeam 801ac4708faSSteve Longerbeam if (link->src.mask) { 802ac4708faSSteve Longerbeam src_reg = ipu_cm_read(ipu, link->src.reg); 803ac4708faSSteve Longerbeam src_reg &= ~link->src.mask; 804ac4708faSSteve Longerbeam src_reg |= link->src.val; 805ac4708faSSteve Longerbeam ipu_cm_write(ipu, src_reg, link->src.reg); 806ac4708faSSteve Longerbeam } 807ac4708faSSteve Longerbeam 808ac4708faSSteve Longerbeam if (link->sink.mask) { 809ac4708faSSteve Longerbeam sink_reg = ipu_cm_read(ipu, link->sink.reg); 810ac4708faSSteve Longerbeam sink_reg &= ~link->sink.mask; 811ac4708faSSteve Longerbeam sink_reg |= link->sink.val; 812ac4708faSSteve Longerbeam ipu_cm_write(ipu, sink_reg, link->sink.reg); 813ac4708faSSteve Longerbeam } 814ac4708faSSteve Longerbeam 815ac4708faSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 816ac4708faSSteve Longerbeam return 0; 817ac4708faSSteve Longerbeam } 818ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_link); 819ac4708faSSteve Longerbeam 820ac4708faSSteve Longerbeam /* 821ac4708faSSteve Longerbeam * Unlinks source and sink channels in the FSU. 822ac4708faSSteve Longerbeam */ 823ac4708faSSteve Longerbeam int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch) 824ac4708faSSteve Longerbeam { 825ac4708faSSteve Longerbeam const struct fsu_link_info *link; 826ac4708faSSteve Longerbeam u32 src_reg, sink_reg; 827ac4708faSSteve Longerbeam unsigned long flags; 828ac4708faSSteve Longerbeam 829ac4708faSSteve Longerbeam link = find_fsu_link_info(src_ch, sink_ch); 830ac4708faSSteve Longerbeam if (!link) 831ac4708faSSteve Longerbeam return -EINVAL; 832ac4708faSSteve Longerbeam 833ac4708faSSteve Longerbeam spin_lock_irqsave(&ipu->lock, flags); 834ac4708faSSteve Longerbeam 835ac4708faSSteve Longerbeam if (link->src.mask) { 836ac4708faSSteve Longerbeam src_reg = ipu_cm_read(ipu, link->src.reg); 837ac4708faSSteve Longerbeam src_reg &= ~link->src.mask; 838ac4708faSSteve Longerbeam ipu_cm_write(ipu, src_reg, link->src.reg); 839ac4708faSSteve Longerbeam } 840ac4708faSSteve Longerbeam 841ac4708faSSteve Longerbeam if (link->sink.mask) { 842ac4708faSSteve Longerbeam sink_reg = ipu_cm_read(ipu, link->sink.reg); 843ac4708faSSteve Longerbeam sink_reg &= ~link->sink.mask; 844ac4708faSSteve Longerbeam ipu_cm_write(ipu, sink_reg, link->sink.reg); 845ac4708faSSteve Longerbeam } 846ac4708faSSteve Longerbeam 847ac4708faSSteve Longerbeam spin_unlock_irqrestore(&ipu->lock, flags); 848ac4708faSSteve Longerbeam return 0; 849ac4708faSSteve Longerbeam } 850ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_fsu_unlink); 851ac4708faSSteve Longerbeam 852ac4708faSSteve Longerbeam /* Link IDMAC channels in the FSU */ 853ac4708faSSteve Longerbeam int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink) 854ac4708faSSteve Longerbeam { 855ac4708faSSteve Longerbeam return ipu_fsu_link(src->ipu, src->num, sink->num); 856ac4708faSSteve Longerbeam } 857ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_link); 858ac4708faSSteve Longerbeam 859ac4708faSSteve Longerbeam /* Unlink IDMAC channels in the FSU */ 860ac4708faSSteve Longerbeam int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink) 861ac4708faSSteve Longerbeam { 862ac4708faSSteve Longerbeam return ipu_fsu_unlink(src->ipu, src->num, sink->num); 863ac4708faSSteve Longerbeam } 864ac4708faSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_idmac_unlink); 865ac4708faSSteve Longerbeam 86639b9004dSPhilipp Zabel struct ipu_devtype { 86739b9004dSPhilipp Zabel const char *name; 86839b9004dSPhilipp Zabel unsigned long cm_ofs; 86939b9004dSPhilipp Zabel unsigned long cpmem_ofs; 87039b9004dSPhilipp Zabel unsigned long srm_ofs; 87139b9004dSPhilipp Zabel unsigned long tpm_ofs; 8722ffd48f2SSteve Longerbeam unsigned long csi0_ofs; 8732ffd48f2SSteve Longerbeam unsigned long csi1_ofs; 8741aa8ea0dSSteve Longerbeam unsigned long ic_ofs; 87539b9004dSPhilipp Zabel unsigned long disp0_ofs; 87639b9004dSPhilipp Zabel unsigned long disp1_ofs; 87739b9004dSPhilipp Zabel unsigned long dc_tmpl_ofs; 87839b9004dSPhilipp Zabel unsigned long vdi_ofs; 87939b9004dSPhilipp Zabel enum ipuv3_type type; 88039b9004dSPhilipp Zabel }; 88139b9004dSPhilipp Zabel 88239b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx51 = { 88339b9004dSPhilipp Zabel .name = "IPUv3EX", 88439b9004dSPhilipp Zabel .cm_ofs = 0x1e000000, 88539b9004dSPhilipp Zabel .cpmem_ofs = 0x1f000000, 88639b9004dSPhilipp Zabel .srm_ofs = 0x1f040000, 88739b9004dSPhilipp Zabel .tpm_ofs = 0x1f060000, 8882ffd48f2SSteve Longerbeam .csi0_ofs = 0x1f030000, 8892ffd48f2SSteve Longerbeam .csi1_ofs = 0x1f038000, 890a49e7c0dSPhilipp Zabel .ic_ofs = 0x1e020000, 89139b9004dSPhilipp Zabel .disp0_ofs = 0x1e040000, 89239b9004dSPhilipp Zabel .disp1_ofs = 0x1e048000, 89339b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x1f080000, 89439b9004dSPhilipp Zabel .vdi_ofs = 0x1e068000, 89539b9004dSPhilipp Zabel .type = IPUV3EX, 89639b9004dSPhilipp Zabel }; 89739b9004dSPhilipp Zabel 89839b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx53 = { 89939b9004dSPhilipp Zabel .name = "IPUv3M", 90039b9004dSPhilipp Zabel .cm_ofs = 0x06000000, 90139b9004dSPhilipp Zabel .cpmem_ofs = 0x07000000, 90239b9004dSPhilipp Zabel .srm_ofs = 0x07040000, 90339b9004dSPhilipp Zabel .tpm_ofs = 0x07060000, 9042ffd48f2SSteve Longerbeam .csi0_ofs = 0x07030000, 9052ffd48f2SSteve Longerbeam .csi1_ofs = 0x07038000, 906a49e7c0dSPhilipp Zabel .ic_ofs = 0x06020000, 90739b9004dSPhilipp Zabel .disp0_ofs = 0x06040000, 90839b9004dSPhilipp Zabel .disp1_ofs = 0x06048000, 90939b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x07080000, 91039b9004dSPhilipp Zabel .vdi_ofs = 0x06068000, 91139b9004dSPhilipp Zabel .type = IPUV3M, 91239b9004dSPhilipp Zabel }; 91339b9004dSPhilipp Zabel 91439b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx6q = { 91539b9004dSPhilipp Zabel .name = "IPUv3H", 91639b9004dSPhilipp Zabel .cm_ofs = 0x00200000, 91739b9004dSPhilipp Zabel .cpmem_ofs = 0x00300000, 91839b9004dSPhilipp Zabel .srm_ofs = 0x00340000, 91939b9004dSPhilipp Zabel .tpm_ofs = 0x00360000, 9202ffd48f2SSteve Longerbeam .csi0_ofs = 0x00230000, 9212ffd48f2SSteve Longerbeam .csi1_ofs = 0x00238000, 9221aa8ea0dSSteve Longerbeam .ic_ofs = 0x00220000, 92339b9004dSPhilipp Zabel .disp0_ofs = 0x00240000, 92439b9004dSPhilipp Zabel .disp1_ofs = 0x00248000, 92539b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x00380000, 92639b9004dSPhilipp Zabel .vdi_ofs = 0x00268000, 92739b9004dSPhilipp Zabel .type = IPUV3H, 92839b9004dSPhilipp Zabel }; 92939b9004dSPhilipp Zabel 93039b9004dSPhilipp Zabel static const struct of_device_id imx_ipu_dt_ids[] = { 93139b9004dSPhilipp Zabel { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, 93239b9004dSPhilipp Zabel { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, 93339b9004dSPhilipp Zabel { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, 93439b9004dSPhilipp Zabel { /* sentinel */ } 93539b9004dSPhilipp Zabel }; 93639b9004dSPhilipp Zabel MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); 93739b9004dSPhilipp Zabel 93839b9004dSPhilipp Zabel static int ipu_submodules_init(struct ipu_soc *ipu, 93939b9004dSPhilipp Zabel struct platform_device *pdev, unsigned long ipu_base, 94039b9004dSPhilipp Zabel struct clk *ipu_clk) 94139b9004dSPhilipp Zabel { 94239b9004dSPhilipp Zabel char *unit; 94339b9004dSPhilipp Zabel int ret; 94439b9004dSPhilipp Zabel struct device *dev = &pdev->dev; 94539b9004dSPhilipp Zabel const struct ipu_devtype *devtype = ipu->devtype; 94639b9004dSPhilipp Zabel 9477d2691daSSteve Longerbeam ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs); 9487d2691daSSteve Longerbeam if (ret) { 9497d2691daSSteve Longerbeam unit = "cpmem"; 9507d2691daSSteve Longerbeam goto err_cpmem; 9517d2691daSSteve Longerbeam } 9527d2691daSSteve Longerbeam 9532ffd48f2SSteve Longerbeam ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs, 9542ffd48f2SSteve Longerbeam IPU_CONF_CSI0_EN, ipu_clk); 9552ffd48f2SSteve Longerbeam if (ret) { 9562ffd48f2SSteve Longerbeam unit = "csi0"; 9572ffd48f2SSteve Longerbeam goto err_csi_0; 9582ffd48f2SSteve Longerbeam } 9592ffd48f2SSteve Longerbeam 9602ffd48f2SSteve Longerbeam ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs, 9612ffd48f2SSteve Longerbeam IPU_CONF_CSI1_EN, ipu_clk); 9622ffd48f2SSteve Longerbeam if (ret) { 9632ffd48f2SSteve Longerbeam unit = "csi1"; 9642ffd48f2SSteve Longerbeam goto err_csi_1; 9652ffd48f2SSteve Longerbeam } 9662ffd48f2SSteve Longerbeam 9671aa8ea0dSSteve Longerbeam ret = ipu_ic_init(ipu, dev, 9681aa8ea0dSSteve Longerbeam ipu_base + devtype->ic_ofs, 9691aa8ea0dSSteve Longerbeam ipu_base + devtype->tpm_ofs); 9701aa8ea0dSSteve Longerbeam if (ret) { 9711aa8ea0dSSteve Longerbeam unit = "ic"; 9721aa8ea0dSSteve Longerbeam goto err_ic; 9731aa8ea0dSSteve Longerbeam } 9741aa8ea0dSSteve Longerbeam 9752d2ead45SSteve Longerbeam ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs, 9762d2ead45SSteve Longerbeam IPU_CONF_VDI_EN | IPU_CONF_ISP_EN | 9772d2ead45SSteve Longerbeam IPU_CONF_IC_INPUT); 9782d2ead45SSteve Longerbeam if (ret) { 9792d2ead45SSteve Longerbeam unit = "vdi"; 9802d2ead45SSteve Longerbeam goto err_vdi; 9812d2ead45SSteve Longerbeam } 9822d2ead45SSteve Longerbeam 983cd98e85aSSteve Longerbeam ret = ipu_image_convert_init(ipu, dev); 984cd98e85aSSteve Longerbeam if (ret) { 985cd98e85aSSteve Longerbeam unit = "image_convert"; 986cd98e85aSSteve Longerbeam goto err_image_convert; 987cd98e85aSSteve Longerbeam } 988cd98e85aSSteve Longerbeam 98939b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, 99039b9004dSPhilipp Zabel IPU_CONF_DI0_EN, ipu_clk); 99139b9004dSPhilipp Zabel if (ret) { 99239b9004dSPhilipp Zabel unit = "di0"; 99339b9004dSPhilipp Zabel goto err_di_0; 99439b9004dSPhilipp Zabel } 99539b9004dSPhilipp Zabel 99639b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs, 99739b9004dSPhilipp Zabel IPU_CONF_DI1_EN, ipu_clk); 99839b9004dSPhilipp Zabel if (ret) { 99939b9004dSPhilipp Zabel unit = "di1"; 100039b9004dSPhilipp Zabel goto err_di_1; 100139b9004dSPhilipp Zabel } 100239b9004dSPhilipp Zabel 100339b9004dSPhilipp Zabel ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs + 100439b9004dSPhilipp Zabel IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs); 100539b9004dSPhilipp Zabel if (ret) { 100639b9004dSPhilipp Zabel unit = "dc_template"; 100739b9004dSPhilipp Zabel goto err_dc; 100839b9004dSPhilipp Zabel } 100939b9004dSPhilipp Zabel 101039b9004dSPhilipp Zabel ret = ipu_dmfc_init(ipu, dev, ipu_base + 101139b9004dSPhilipp Zabel devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk); 101239b9004dSPhilipp Zabel if (ret) { 101339b9004dSPhilipp Zabel unit = "dmfc"; 101439b9004dSPhilipp Zabel goto err_dmfc; 101539b9004dSPhilipp Zabel } 101639b9004dSPhilipp Zabel 101739b9004dSPhilipp Zabel ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs); 101839b9004dSPhilipp Zabel if (ret) { 101939b9004dSPhilipp Zabel unit = "dp"; 102039b9004dSPhilipp Zabel goto err_dp; 102139b9004dSPhilipp Zabel } 102239b9004dSPhilipp Zabel 102335de925fSPhilipp Zabel ret = ipu_smfc_init(ipu, dev, ipu_base + 102435de925fSPhilipp Zabel devtype->cm_ofs + IPU_CM_SMFC_REG_OFS); 102535de925fSPhilipp Zabel if (ret) { 102635de925fSPhilipp Zabel unit = "smfc"; 102735de925fSPhilipp Zabel goto err_smfc; 102835de925fSPhilipp Zabel } 102935de925fSPhilipp Zabel 103039b9004dSPhilipp Zabel return 0; 103139b9004dSPhilipp Zabel 103235de925fSPhilipp Zabel err_smfc: 103335de925fSPhilipp Zabel ipu_dp_exit(ipu); 103439b9004dSPhilipp Zabel err_dp: 103539b9004dSPhilipp Zabel ipu_dmfc_exit(ipu); 103639b9004dSPhilipp Zabel err_dmfc: 103739b9004dSPhilipp Zabel ipu_dc_exit(ipu); 103839b9004dSPhilipp Zabel err_dc: 103939b9004dSPhilipp Zabel ipu_di_exit(ipu, 1); 104039b9004dSPhilipp Zabel err_di_1: 104139b9004dSPhilipp Zabel ipu_di_exit(ipu, 0); 104239b9004dSPhilipp Zabel err_di_0: 1043cd98e85aSSteve Longerbeam ipu_image_convert_exit(ipu); 1044cd98e85aSSteve Longerbeam err_image_convert: 10452d2ead45SSteve Longerbeam ipu_vdi_exit(ipu); 10462d2ead45SSteve Longerbeam err_vdi: 10471aa8ea0dSSteve Longerbeam ipu_ic_exit(ipu); 10481aa8ea0dSSteve Longerbeam err_ic: 10492ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 1); 10502ffd48f2SSteve Longerbeam err_csi_1: 10512ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 0); 10522ffd48f2SSteve Longerbeam err_csi_0: 10537d2691daSSteve Longerbeam ipu_cpmem_exit(ipu); 10547d2691daSSteve Longerbeam err_cpmem: 105539b9004dSPhilipp Zabel dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); 105639b9004dSPhilipp Zabel return ret; 105739b9004dSPhilipp Zabel } 105839b9004dSPhilipp Zabel 105939b9004dSPhilipp Zabel static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) 106039b9004dSPhilipp Zabel { 106139b9004dSPhilipp Zabel unsigned long status; 106239b9004dSPhilipp Zabel int i, bit, irq; 106339b9004dSPhilipp Zabel 106439b9004dSPhilipp Zabel for (i = 0; i < num_regs; i++) { 106539b9004dSPhilipp Zabel 106639b9004dSPhilipp Zabel status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i])); 106739b9004dSPhilipp Zabel status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i])); 106839b9004dSPhilipp Zabel 106939b9004dSPhilipp Zabel for_each_set_bit(bit, &status, 32) { 1070682b7c1cSLinus Torvalds irq = irq_linear_revmap(ipu->domain, 1071682b7c1cSLinus Torvalds regs[i] * 32 + bit); 107239b9004dSPhilipp Zabel if (irq) 107339b9004dSPhilipp Zabel generic_handle_irq(irq); 107439b9004dSPhilipp Zabel } 107539b9004dSPhilipp Zabel } 107639b9004dSPhilipp Zabel } 107739b9004dSPhilipp Zabel 1078bd0b9ac4SThomas Gleixner static void ipu_irq_handler(struct irq_desc *desc) 107939b9004dSPhilipp Zabel { 108039b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 10814d9efdfcSJiang Liu struct irq_chip *chip = irq_desc_get_chip(desc); 108239b9004dSPhilipp Zabel const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14}; 108339b9004dSPhilipp Zabel 108439b9004dSPhilipp Zabel chained_irq_enter(chip, desc); 108539b9004dSPhilipp Zabel 108639b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 108739b9004dSPhilipp Zabel 108839b9004dSPhilipp Zabel chained_irq_exit(chip, desc); 108939b9004dSPhilipp Zabel } 109039b9004dSPhilipp Zabel 1091bd0b9ac4SThomas Gleixner static void ipu_err_irq_handler(struct irq_desc *desc) 109239b9004dSPhilipp Zabel { 109339b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 10944d9efdfcSJiang Liu struct irq_chip *chip = irq_desc_get_chip(desc); 109539b9004dSPhilipp Zabel const int int_reg[] = { 4, 5, 8, 9}; 109639b9004dSPhilipp Zabel 109739b9004dSPhilipp Zabel chained_irq_enter(chip, desc); 109839b9004dSPhilipp Zabel 109939b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 110039b9004dSPhilipp Zabel 110139b9004dSPhilipp Zabel chained_irq_exit(chip, desc); 110239b9004dSPhilipp Zabel } 110339b9004dSPhilipp Zabel 1104682b7c1cSLinus Torvalds int ipu_map_irq(struct ipu_soc *ipu, int irq) 1105682b7c1cSLinus Torvalds { 1106682b7c1cSLinus Torvalds int virq; 1107682b7c1cSLinus Torvalds 1108682b7c1cSLinus Torvalds virq = irq_linear_revmap(ipu->domain, irq); 1109682b7c1cSLinus Torvalds if (!virq) 1110682b7c1cSLinus Torvalds virq = irq_create_mapping(ipu->domain, irq); 1111682b7c1cSLinus Torvalds 1112682b7c1cSLinus Torvalds return virq; 1113682b7c1cSLinus Torvalds } 1114682b7c1cSLinus Torvalds EXPORT_SYMBOL_GPL(ipu_map_irq); 1115682b7c1cSLinus Torvalds 111639b9004dSPhilipp Zabel int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, 111739b9004dSPhilipp Zabel enum ipu_channel_irq irq_type) 111839b9004dSPhilipp Zabel { 1119682b7c1cSLinus Torvalds return ipu_map_irq(ipu, irq_type + channel->num); 112039b9004dSPhilipp Zabel } 112139b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); 112239b9004dSPhilipp Zabel 112339b9004dSPhilipp Zabel static void ipu_submodules_exit(struct ipu_soc *ipu) 112439b9004dSPhilipp Zabel { 112535de925fSPhilipp Zabel ipu_smfc_exit(ipu); 112639b9004dSPhilipp Zabel ipu_dp_exit(ipu); 112739b9004dSPhilipp Zabel ipu_dmfc_exit(ipu); 112839b9004dSPhilipp Zabel ipu_dc_exit(ipu); 112939b9004dSPhilipp Zabel ipu_di_exit(ipu, 1); 113039b9004dSPhilipp Zabel ipu_di_exit(ipu, 0); 1131cd98e85aSSteve Longerbeam ipu_image_convert_exit(ipu); 11322d2ead45SSteve Longerbeam ipu_vdi_exit(ipu); 11331aa8ea0dSSteve Longerbeam ipu_ic_exit(ipu); 11342ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 1); 11352ffd48f2SSteve Longerbeam ipu_csi_exit(ipu, 0); 11367d2691daSSteve Longerbeam ipu_cpmem_exit(ipu); 113739b9004dSPhilipp Zabel } 113839b9004dSPhilipp Zabel 113939b9004dSPhilipp Zabel static int platform_remove_devices_fn(struct device *dev, void *unused) 114039b9004dSPhilipp Zabel { 114139b9004dSPhilipp Zabel struct platform_device *pdev = to_platform_device(dev); 114239b9004dSPhilipp Zabel 114339b9004dSPhilipp Zabel platform_device_unregister(pdev); 114439b9004dSPhilipp Zabel 114539b9004dSPhilipp Zabel return 0; 114639b9004dSPhilipp Zabel } 114739b9004dSPhilipp Zabel 114839b9004dSPhilipp Zabel static void platform_device_unregister_children(struct platform_device *pdev) 114939b9004dSPhilipp Zabel { 115039b9004dSPhilipp Zabel device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn); 115139b9004dSPhilipp Zabel } 115239b9004dSPhilipp Zabel 115339b9004dSPhilipp Zabel struct ipu_platform_reg { 115439b9004dSPhilipp Zabel struct ipu_client_platformdata pdata; 115539b9004dSPhilipp Zabel const char *name; 115639b9004dSPhilipp Zabel }; 115739b9004dSPhilipp Zabel 1158304e6be6SPhilipp Zabel /* These must be in the order of the corresponding device tree port nodes */ 1159310944d1SPhilipp Zabel static struct ipu_platform_reg client_reg[] = { 116039b9004dSPhilipp Zabel { 116139b9004dSPhilipp Zabel .pdata = { 1162304e6be6SPhilipp Zabel .csi = 0, 1163304e6be6SPhilipp Zabel .dma[0] = IPUV3_CHANNEL_CSI0, 1164304e6be6SPhilipp Zabel .dma[1] = -EINVAL, 1165304e6be6SPhilipp Zabel }, 116688287ec3SSteve Longerbeam .name = "imx-ipuv3-csi", 1167304e6be6SPhilipp Zabel }, { 1168304e6be6SPhilipp Zabel .pdata = { 1169304e6be6SPhilipp Zabel .csi = 1, 1170304e6be6SPhilipp Zabel .dma[0] = IPUV3_CHANNEL_CSI1, 1171304e6be6SPhilipp Zabel .dma[1] = -EINVAL, 1172304e6be6SPhilipp Zabel }, 117388287ec3SSteve Longerbeam .name = "imx-ipuv3-csi", 1174304e6be6SPhilipp Zabel }, { 1175304e6be6SPhilipp Zabel .pdata = { 117639b9004dSPhilipp Zabel .di = 0, 117739b9004dSPhilipp Zabel .dc = 5, 117839b9004dSPhilipp Zabel .dp = IPU_DP_FLOW_SYNC_BG, 117939b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC, 118039b9004dSPhilipp Zabel .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC, 118139b9004dSPhilipp Zabel }, 118239b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc", 118339b9004dSPhilipp Zabel }, { 118439b9004dSPhilipp Zabel .pdata = { 118539b9004dSPhilipp Zabel .di = 1, 118639b9004dSPhilipp Zabel .dc = 1, 118739b9004dSPhilipp Zabel .dp = -EINVAL, 118839b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC, 118939b9004dSPhilipp Zabel .dma[1] = -EINVAL, 119039b9004dSPhilipp Zabel }, 119139b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc", 119239b9004dSPhilipp Zabel }, 119339b9004dSPhilipp Zabel }; 119439b9004dSPhilipp Zabel 119539b9004dSPhilipp Zabel static DEFINE_MUTEX(ipu_client_id_mutex); 119639b9004dSPhilipp Zabel static int ipu_client_id; 119739b9004dSPhilipp Zabel 1198d6ca8ca7SPhilipp Zabel static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) 119939b9004dSPhilipp Zabel { 120039b9004dSPhilipp Zabel struct device *dev = ipu->dev; 120139b9004dSPhilipp Zabel unsigned i; 120239b9004dSPhilipp Zabel int id, ret; 120339b9004dSPhilipp Zabel 120439b9004dSPhilipp Zabel mutex_lock(&ipu_client_id_mutex); 120539b9004dSPhilipp Zabel id = ipu_client_id; 120639b9004dSPhilipp Zabel ipu_client_id += ARRAY_SIZE(client_reg); 120739b9004dSPhilipp Zabel mutex_unlock(&ipu_client_id_mutex); 120839b9004dSPhilipp Zabel 120939b9004dSPhilipp Zabel for (i = 0; i < ARRAY_SIZE(client_reg); i++) { 1210310944d1SPhilipp Zabel struct ipu_platform_reg *reg = &client_reg[i]; 121139b9004dSPhilipp Zabel struct platform_device *pdev; 121217e05217SPhilipp Zabel struct device_node *of_node; 121317e05217SPhilipp Zabel 121417e05217SPhilipp Zabel /* Associate subdevice with the corresponding port node */ 121517e05217SPhilipp Zabel of_node = of_graph_get_port_by_id(dev->of_node, i); 121617e05217SPhilipp Zabel if (!of_node) { 121717e05217SPhilipp Zabel dev_info(dev, 121817e05217SPhilipp Zabel "no port@%d node in %s, not using %s%d\n", 121917e05217SPhilipp Zabel i, dev->of_node->full_name, 122017e05217SPhilipp Zabel (i / 2) ? "DI" : "CSI", i % 2); 122117e05217SPhilipp Zabel continue; 122217e05217SPhilipp Zabel } 122339b9004dSPhilipp Zabel 1224304e6be6SPhilipp Zabel pdev = platform_device_alloc(reg->name, id++); 1225304e6be6SPhilipp Zabel if (!pdev) { 1226304e6be6SPhilipp Zabel ret = -ENOMEM; 1227304e6be6SPhilipp Zabel goto err_register; 1228304e6be6SPhilipp Zabel } 122939b9004dSPhilipp Zabel 1230304e6be6SPhilipp Zabel pdev->dev.parent = dev; 1231304e6be6SPhilipp Zabel 1232310944d1SPhilipp Zabel reg->pdata.of_node = of_node; 1233304e6be6SPhilipp Zabel ret = platform_device_add_data(pdev, ®->pdata, 1234304e6be6SPhilipp Zabel sizeof(reg->pdata)); 1235304e6be6SPhilipp Zabel if (!ret) 1236304e6be6SPhilipp Zabel ret = platform_device_add(pdev); 1237304e6be6SPhilipp Zabel if (ret) { 1238304e6be6SPhilipp Zabel platform_device_put(pdev); 123939b9004dSPhilipp Zabel goto err_register; 124039b9004dSPhilipp Zabel } 1241503fe87bSPhilipp Zabel 1242503fe87bSPhilipp Zabel /* 1243503fe87bSPhilipp Zabel * Set of_node only after calling platform_device_add. Otherwise 1244503fe87bSPhilipp Zabel * the platform:imx-ipuv3-crtc modalias won't be used. 1245503fe87bSPhilipp Zabel */ 1246503fe87bSPhilipp Zabel pdev->dev.of_node = of_node; 1247e4946cdcSAxel Lin } 124839b9004dSPhilipp Zabel 124939b9004dSPhilipp Zabel return 0; 125039b9004dSPhilipp Zabel 125139b9004dSPhilipp Zabel err_register: 125239b9004dSPhilipp Zabel platform_device_unregister_children(to_platform_device(dev)); 125339b9004dSPhilipp Zabel 125439b9004dSPhilipp Zabel return ret; 125539b9004dSPhilipp Zabel } 125639b9004dSPhilipp Zabel 125739b9004dSPhilipp Zabel 125839b9004dSPhilipp Zabel static int ipu_irq_init(struct ipu_soc *ipu) 125939b9004dSPhilipp Zabel { 126039b9004dSPhilipp Zabel struct irq_chip_generic *gc; 126139b9004dSPhilipp Zabel struct irq_chip_type *ct; 126239b9004dSPhilipp Zabel unsigned long unused[IPU_NUM_IRQS / 32] = { 126339b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 126439b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 126539b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 126639b9004dSPhilipp Zabel 0x4077ffff, 0xffe7e1fd, 126739b9004dSPhilipp Zabel 0x23fffffe, 0x8880fff0, 126839b9004dSPhilipp Zabel 0xf98fe7d0, 0xfff81fff, 126939b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 127039b9004dSPhilipp Zabel 0x00000000, 127139b9004dSPhilipp Zabel }; 127239b9004dSPhilipp Zabel int ret, i; 127339b9004dSPhilipp Zabel 127439b9004dSPhilipp Zabel ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS, 127539b9004dSPhilipp Zabel &irq_generic_chip_ops, ipu); 127639b9004dSPhilipp Zabel if (!ipu->domain) { 127739b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to add irq domain\n"); 127839b9004dSPhilipp Zabel return -ENODEV; 127939b9004dSPhilipp Zabel } 128039b9004dSPhilipp Zabel 128139b9004dSPhilipp Zabel ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", 1282ca0141deSRob Herring handle_level_irq, 0, 0, 0); 128339b9004dSPhilipp Zabel if (ret < 0) { 128439b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to alloc generic irq chips\n"); 128539b9004dSPhilipp Zabel irq_domain_remove(ipu->domain); 128639b9004dSPhilipp Zabel return ret; 128739b9004dSPhilipp Zabel } 128839b9004dSPhilipp Zabel 1289a92d8145SPhilipp Zabel /* Mask and clear all interrupts */ 1290a92d8145SPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i += 32) { 1291510e6426SRussell King ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32)); 1292a92d8145SPhilipp Zabel ipu_cm_write(ipu, ~unused[i / 32], IPU_INT_STAT(i / 32)); 1293a92d8145SPhilipp Zabel } 1294510e6426SRussell King 129539b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i += 32) { 129639b9004dSPhilipp Zabel gc = irq_get_domain_generic_chip(ipu->domain, i); 129739b9004dSPhilipp Zabel gc->reg_base = ipu->cm_reg; 129839b9004dSPhilipp Zabel gc->unused = unused[i / 32]; 129939b9004dSPhilipp Zabel ct = gc->chip_types; 130039b9004dSPhilipp Zabel ct->chip.irq_ack = irq_gc_ack_set_bit; 130139b9004dSPhilipp Zabel ct->chip.irq_mask = irq_gc_mask_clr_bit; 130239b9004dSPhilipp Zabel ct->chip.irq_unmask = irq_gc_mask_set_bit; 130339b9004dSPhilipp Zabel ct->regs.ack = IPU_INT_STAT(i / 32); 130439b9004dSPhilipp Zabel ct->regs.mask = IPU_INT_CTRL(i / 32); 130539b9004dSPhilipp Zabel } 130639b9004dSPhilipp Zabel 130786f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu); 130886f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler, 130986f5e733SRussell King ipu); 131039b9004dSPhilipp Zabel 131139b9004dSPhilipp Zabel return 0; 131239b9004dSPhilipp Zabel } 131339b9004dSPhilipp Zabel 131439b9004dSPhilipp Zabel static void ipu_irq_exit(struct ipu_soc *ipu) 131539b9004dSPhilipp Zabel { 131639b9004dSPhilipp Zabel int i, irq; 131739b9004dSPhilipp Zabel 131886f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL); 131986f5e733SRussell King irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL); 132039b9004dSPhilipp Zabel 132139b9004dSPhilipp Zabel /* TODO: remove irq_domain_generic_chips */ 132239b9004dSPhilipp Zabel 132339b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i++) { 132439b9004dSPhilipp Zabel irq = irq_linear_revmap(ipu->domain, i); 132539b9004dSPhilipp Zabel if (irq) 132639b9004dSPhilipp Zabel irq_dispose_mapping(irq); 132739b9004dSPhilipp Zabel } 132839b9004dSPhilipp Zabel 132939b9004dSPhilipp Zabel irq_domain_remove(ipu->domain); 133039b9004dSPhilipp Zabel } 133139b9004dSPhilipp Zabel 13323feb049fSSteve Longerbeam void ipu_dump(struct ipu_soc *ipu) 13333feb049fSSteve Longerbeam { 13343feb049fSSteve Longerbeam int i; 13353feb049fSSteve Longerbeam 13363feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n", 13373feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CONF)); 13383feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n", 13393feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CONF)); 13403feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n", 13413feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_EN(0))); 13423feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n", 13433feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_EN(32))); 13443feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n", 13453feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_PRI(0))); 13463feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n", 13473feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_CHA_PRI(32))); 13483feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n", 13493feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_BAND_EN(0))); 13503feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n", 13513feb049fSSteve Longerbeam ipu_idmac_read(ipu, IDMAC_BAND_EN(32))); 13523feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", 13533feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0))); 13543feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", 13553feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32))); 13563feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n", 13573feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW1)); 13583feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n", 13593feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW2)); 13603feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n", 13613feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_PROC_FLOW3)); 13623feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n", 13633feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_FS_DISP_FLOW1)); 13643feb049fSSteve Longerbeam for (i = 0; i < 15; i++) 13653feb049fSSteve Longerbeam dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i, 13663feb049fSSteve Longerbeam ipu_cm_read(ipu, IPU_INT_CTRL(i))); 13673feb049fSSteve Longerbeam } 13683feb049fSSteve Longerbeam EXPORT_SYMBOL_GPL(ipu_dump); 13693feb049fSSteve Longerbeam 137039b9004dSPhilipp Zabel static int ipu_probe(struct platform_device *pdev) 137139b9004dSPhilipp Zabel { 1372572a7615SSteve Longerbeam struct device_node *np = pdev->dev.of_node; 137339b9004dSPhilipp Zabel struct ipu_soc *ipu; 137439b9004dSPhilipp Zabel struct resource *res; 137539b9004dSPhilipp Zabel unsigned long ipu_base; 137639b9004dSPhilipp Zabel int i, ret, irq_sync, irq_err; 137739b9004dSPhilipp Zabel const struct ipu_devtype *devtype; 137839b9004dSPhilipp Zabel 1379e92e4478SLABBE Corentin devtype = of_device_get_match_data(&pdev->dev); 1380e92e4478SLABBE Corentin if (!devtype) 1381e92e4478SLABBE Corentin return -EINVAL; 138239b9004dSPhilipp Zabel 138339b9004dSPhilipp Zabel irq_sync = platform_get_irq(pdev, 0); 138439b9004dSPhilipp Zabel irq_err = platform_get_irq(pdev, 1); 138539b9004dSPhilipp Zabel res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 138639b9004dSPhilipp Zabel 138739b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n", 138839b9004dSPhilipp Zabel irq_sync, irq_err); 138939b9004dSPhilipp Zabel 139039b9004dSPhilipp Zabel if (!res || irq_sync < 0 || irq_err < 0) 139139b9004dSPhilipp Zabel return -ENODEV; 139239b9004dSPhilipp Zabel 139339b9004dSPhilipp Zabel ipu_base = res->start; 139439b9004dSPhilipp Zabel 139539b9004dSPhilipp Zabel ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL); 139639b9004dSPhilipp Zabel if (!ipu) 139739b9004dSPhilipp Zabel return -ENODEV; 139839b9004dSPhilipp Zabel 139939b9004dSPhilipp Zabel for (i = 0; i < 64; i++) 140039b9004dSPhilipp Zabel ipu->channel[i].ipu = ipu; 140139b9004dSPhilipp Zabel ipu->devtype = devtype; 140239b9004dSPhilipp Zabel ipu->ipu_type = devtype->type; 1403572a7615SSteve Longerbeam ipu->id = of_alias_get_id(np, "ipu"); 140439b9004dSPhilipp Zabel 140539b9004dSPhilipp Zabel spin_lock_init(&ipu->lock); 140639b9004dSPhilipp Zabel mutex_init(&ipu->channel_lock); 140739b9004dSPhilipp Zabel 140839b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n", 140939b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs); 141039b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "idmac: 0x%08lx\n", 141139b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS); 141239b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n", 141339b9004dSPhilipp Zabel ipu_base + devtype->cpmem_ofs); 14142ffd48f2SSteve Longerbeam dev_dbg(&pdev->dev, "csi0: 0x%08lx\n", 14152ffd48f2SSteve Longerbeam ipu_base + devtype->csi0_ofs); 14162ffd48f2SSteve Longerbeam dev_dbg(&pdev->dev, "csi1: 0x%08lx\n", 14172ffd48f2SSteve Longerbeam ipu_base + devtype->csi1_ofs); 14181aa8ea0dSSteve Longerbeam dev_dbg(&pdev->dev, "ic: 0x%08lx\n", 14191aa8ea0dSSteve Longerbeam ipu_base + devtype->ic_ofs); 142039b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp0: 0x%08lx\n", 142139b9004dSPhilipp Zabel ipu_base + devtype->disp0_ofs); 142239b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp1: 0x%08lx\n", 142339b9004dSPhilipp Zabel ipu_base + devtype->disp1_ofs); 142439b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "srm: 0x%08lx\n", 142539b9004dSPhilipp Zabel ipu_base + devtype->srm_ofs); 142639b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "tpm: 0x%08lx\n", 142739b9004dSPhilipp Zabel ipu_base + devtype->tpm_ofs); 142839b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dc: 0x%08lx\n", 142939b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS); 143039b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "ic: 0x%08lx\n", 143139b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS); 143239b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n", 143339b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS); 143439b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "vdi: 0x%08lx\n", 143539b9004dSPhilipp Zabel ipu_base + devtype->vdi_ofs); 143639b9004dSPhilipp Zabel 143739b9004dSPhilipp Zabel ipu->cm_reg = devm_ioremap(&pdev->dev, 143839b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs, PAGE_SIZE); 143939b9004dSPhilipp Zabel ipu->idmac_reg = devm_ioremap(&pdev->dev, 144039b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, 144139b9004dSPhilipp Zabel PAGE_SIZE); 144239b9004dSPhilipp Zabel 14437d2691daSSteve Longerbeam if (!ipu->cm_reg || !ipu->idmac_reg) 144439b9004dSPhilipp Zabel return -ENOMEM; 144539b9004dSPhilipp Zabel 144639b9004dSPhilipp Zabel ipu->clk = devm_clk_get(&pdev->dev, "bus"); 144739b9004dSPhilipp Zabel if (IS_ERR(ipu->clk)) { 144839b9004dSPhilipp Zabel ret = PTR_ERR(ipu->clk); 144939b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_get failed with %d", ret); 145039b9004dSPhilipp Zabel return ret; 145139b9004dSPhilipp Zabel } 145239b9004dSPhilipp Zabel 145339b9004dSPhilipp Zabel platform_set_drvdata(pdev, ipu); 145439b9004dSPhilipp Zabel 145539b9004dSPhilipp Zabel ret = clk_prepare_enable(ipu->clk); 145639b9004dSPhilipp Zabel if (ret) { 145739b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); 145839b9004dSPhilipp Zabel return ret; 145939b9004dSPhilipp Zabel } 146039b9004dSPhilipp Zabel 146139b9004dSPhilipp Zabel ipu->dev = &pdev->dev; 146239b9004dSPhilipp Zabel ipu->irq_sync = irq_sync; 146339b9004dSPhilipp Zabel ipu->irq_err = irq_err; 146439b9004dSPhilipp Zabel 146539b9004dSPhilipp Zabel ret = device_reset(&pdev->dev); 146639b9004dSPhilipp Zabel if (ret) { 146739b9004dSPhilipp Zabel dev_err(&pdev->dev, "failed to reset: %d\n", ret); 146839b9004dSPhilipp Zabel goto out_failed_reset; 146939b9004dSPhilipp Zabel } 147039b9004dSPhilipp Zabel ret = ipu_memory_reset(ipu); 147139b9004dSPhilipp Zabel if (ret) 147239b9004dSPhilipp Zabel goto out_failed_reset; 147339b9004dSPhilipp Zabel 1474596a65d1SDavid Jander ret = ipu_irq_init(ipu); 1475596a65d1SDavid Jander if (ret) 1476596a65d1SDavid Jander goto out_failed_irq; 1477596a65d1SDavid Jander 147839b9004dSPhilipp Zabel /* Set MCU_T to divide MCU access window into 2 */ 147939b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), 148039b9004dSPhilipp Zabel IPU_DISP_GEN); 148139b9004dSPhilipp Zabel 148239b9004dSPhilipp Zabel ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk); 148339b9004dSPhilipp Zabel if (ret) 148439b9004dSPhilipp Zabel goto failed_submodules_init; 148539b9004dSPhilipp Zabel 1486d6ca8ca7SPhilipp Zabel ret = ipu_add_client_devices(ipu, ipu_base); 148739b9004dSPhilipp Zabel if (ret) { 148839b9004dSPhilipp Zabel dev_err(&pdev->dev, "adding client devices failed with %d\n", 148939b9004dSPhilipp Zabel ret); 149039b9004dSPhilipp Zabel goto failed_add_clients; 149139b9004dSPhilipp Zabel } 149239b9004dSPhilipp Zabel 149339b9004dSPhilipp Zabel dev_info(&pdev->dev, "%s probed\n", devtype->name); 149439b9004dSPhilipp Zabel 149539b9004dSPhilipp Zabel return 0; 149639b9004dSPhilipp Zabel 149739b9004dSPhilipp Zabel failed_add_clients: 149839b9004dSPhilipp Zabel ipu_submodules_exit(ipu); 149939b9004dSPhilipp Zabel failed_submodules_init: 150039b9004dSPhilipp Zabel ipu_irq_exit(ipu); 150139b9004dSPhilipp Zabel out_failed_irq: 1502596a65d1SDavid Jander out_failed_reset: 150339b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk); 150439b9004dSPhilipp Zabel return ret; 150539b9004dSPhilipp Zabel } 150639b9004dSPhilipp Zabel 150739b9004dSPhilipp Zabel static int ipu_remove(struct platform_device *pdev) 150839b9004dSPhilipp Zabel { 150939b9004dSPhilipp Zabel struct ipu_soc *ipu = platform_get_drvdata(pdev); 151039b9004dSPhilipp Zabel 151139b9004dSPhilipp Zabel platform_device_unregister_children(pdev); 151239b9004dSPhilipp Zabel ipu_submodules_exit(ipu); 151339b9004dSPhilipp Zabel ipu_irq_exit(ipu); 151439b9004dSPhilipp Zabel 151539b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk); 151639b9004dSPhilipp Zabel 151739b9004dSPhilipp Zabel return 0; 151839b9004dSPhilipp Zabel } 151939b9004dSPhilipp Zabel 152039b9004dSPhilipp Zabel static struct platform_driver imx_ipu_driver = { 152139b9004dSPhilipp Zabel .driver = { 152239b9004dSPhilipp Zabel .name = "imx-ipuv3", 152339b9004dSPhilipp Zabel .of_match_table = imx_ipu_dt_ids, 152439b9004dSPhilipp Zabel }, 152539b9004dSPhilipp Zabel .probe = ipu_probe, 152639b9004dSPhilipp Zabel .remove = ipu_remove, 152739b9004dSPhilipp Zabel }; 152839b9004dSPhilipp Zabel 152939b9004dSPhilipp Zabel module_platform_driver(imx_ipu_driver); 153039b9004dSPhilipp Zabel 153139b9004dSPhilipp Zabel MODULE_ALIAS("platform:imx-ipuv3"); 153239b9004dSPhilipp Zabel MODULE_DESCRIPTION("i.MX IPU v3 driver"); 153339b9004dSPhilipp Zabel MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 153439b9004dSPhilipp Zabel MODULE_LICENSE("GPL"); 1535