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> 3139b9004dSPhilipp Zabel 3239b9004dSPhilipp Zabel #include <drm/drm_fourcc.h> 3339b9004dSPhilipp Zabel 3439b9004dSPhilipp Zabel #include <video/imx-ipu-v3.h> 3539b9004dSPhilipp Zabel #include "ipu-prv.h" 3639b9004dSPhilipp Zabel 3739b9004dSPhilipp Zabel static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset) 3839b9004dSPhilipp Zabel { 3939b9004dSPhilipp Zabel return readl(ipu->cm_reg + offset); 4039b9004dSPhilipp Zabel } 4139b9004dSPhilipp Zabel 4239b9004dSPhilipp Zabel static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset) 4339b9004dSPhilipp Zabel { 4439b9004dSPhilipp Zabel writel(value, ipu->cm_reg + offset); 4539b9004dSPhilipp Zabel } 4639b9004dSPhilipp Zabel 4739b9004dSPhilipp Zabel static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset) 4839b9004dSPhilipp Zabel { 4939b9004dSPhilipp Zabel return readl(ipu->idmac_reg + offset); 5039b9004dSPhilipp Zabel } 5139b9004dSPhilipp Zabel 5239b9004dSPhilipp Zabel static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value, 5339b9004dSPhilipp Zabel unsigned offset) 5439b9004dSPhilipp Zabel { 5539b9004dSPhilipp Zabel writel(value, ipu->idmac_reg + offset); 5639b9004dSPhilipp Zabel } 5739b9004dSPhilipp Zabel 5839b9004dSPhilipp Zabel void ipu_srm_dp_sync_update(struct ipu_soc *ipu) 5939b9004dSPhilipp Zabel { 6039b9004dSPhilipp Zabel u32 val; 6139b9004dSPhilipp Zabel 6239b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_SRM_PRI2); 6339b9004dSPhilipp Zabel val |= 0x8; 6439b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_SRM_PRI2); 6539b9004dSPhilipp Zabel } 6639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_srm_dp_sync_update); 6739b9004dSPhilipp Zabel 6839b9004dSPhilipp Zabel struct ipu_ch_param __iomem *ipu_get_cpmem(struct ipuv3_channel *channel) 6939b9004dSPhilipp Zabel { 7039b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 7139b9004dSPhilipp Zabel 7239b9004dSPhilipp Zabel return ipu->cpmem_base + channel->num; 7339b9004dSPhilipp Zabel } 7439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_get_cpmem); 7539b9004dSPhilipp Zabel 7639b9004dSPhilipp Zabel void ipu_cpmem_set_high_priority(struct ipuv3_channel *channel) 7739b9004dSPhilipp Zabel { 7839b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 7939b9004dSPhilipp Zabel struct ipu_ch_param __iomem *p = ipu_get_cpmem(channel); 8039b9004dSPhilipp Zabel u32 val; 8139b9004dSPhilipp Zabel 8239b9004dSPhilipp Zabel if (ipu->ipu_type == IPUV3EX) 8339b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_ID, 1); 8439b9004dSPhilipp Zabel 8539b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(channel->num)); 8639b9004dSPhilipp Zabel val |= 1 << (channel->num % 32); 8739b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(channel->num)); 8839b9004dSPhilipp Zabel }; 8939b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority); 9039b9004dSPhilipp Zabel 9139b9004dSPhilipp Zabel void ipu_ch_param_write_field(struct ipu_ch_param __iomem *base, u32 wbs, u32 v) 9239b9004dSPhilipp Zabel { 9339b9004dSPhilipp Zabel u32 bit = (wbs >> 8) % 160; 9439b9004dSPhilipp Zabel u32 size = wbs & 0xff; 9539b9004dSPhilipp Zabel u32 word = (wbs >> 8) / 160; 9639b9004dSPhilipp Zabel u32 i = bit / 32; 9739b9004dSPhilipp Zabel u32 ofs = bit % 32; 9839b9004dSPhilipp Zabel u32 mask = (1 << size) - 1; 9939b9004dSPhilipp Zabel u32 val; 10039b9004dSPhilipp Zabel 10139b9004dSPhilipp Zabel pr_debug("%s %d %d %d\n", __func__, word, bit , size); 10239b9004dSPhilipp Zabel 10339b9004dSPhilipp Zabel val = readl(&base->word[word].data[i]); 10439b9004dSPhilipp Zabel val &= ~(mask << ofs); 10539b9004dSPhilipp Zabel val |= v << ofs; 10639b9004dSPhilipp Zabel writel(val, &base->word[word].data[i]); 10739b9004dSPhilipp Zabel 10839b9004dSPhilipp Zabel if ((bit + size - 1) / 32 > i) { 10939b9004dSPhilipp Zabel val = readl(&base->word[word].data[i + 1]); 11039b9004dSPhilipp Zabel val &= ~(mask >> (ofs ? (32 - ofs) : 0)); 11139b9004dSPhilipp Zabel val |= v >> (ofs ? (32 - ofs) : 0); 11239b9004dSPhilipp Zabel writel(val, &base->word[word].data[i + 1]); 11339b9004dSPhilipp Zabel } 11439b9004dSPhilipp Zabel } 11539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_ch_param_write_field); 11639b9004dSPhilipp Zabel 11739b9004dSPhilipp Zabel u32 ipu_ch_param_read_field(struct ipu_ch_param __iomem *base, u32 wbs) 11839b9004dSPhilipp Zabel { 11939b9004dSPhilipp Zabel u32 bit = (wbs >> 8) % 160; 12039b9004dSPhilipp Zabel u32 size = wbs & 0xff; 12139b9004dSPhilipp Zabel u32 word = (wbs >> 8) / 160; 12239b9004dSPhilipp Zabel u32 i = bit / 32; 12339b9004dSPhilipp Zabel u32 ofs = bit % 32; 12439b9004dSPhilipp Zabel u32 mask = (1 << size) - 1; 12539b9004dSPhilipp Zabel u32 val = 0; 12639b9004dSPhilipp Zabel 12739b9004dSPhilipp Zabel pr_debug("%s %d %d %d\n", __func__, word, bit , size); 12839b9004dSPhilipp Zabel 12939b9004dSPhilipp Zabel val = (readl(&base->word[word].data[i]) >> ofs) & mask; 13039b9004dSPhilipp Zabel 13139b9004dSPhilipp Zabel if ((bit + size - 1) / 32 > i) { 13239b9004dSPhilipp Zabel u32 tmp; 13339b9004dSPhilipp Zabel tmp = readl(&base->word[word].data[i + 1]); 13439b9004dSPhilipp Zabel tmp &= mask >> (ofs ? (32 - ofs) : 0); 13539b9004dSPhilipp Zabel val |= tmp << (ofs ? (32 - ofs) : 0); 13639b9004dSPhilipp Zabel } 13739b9004dSPhilipp Zabel 13839b9004dSPhilipp Zabel return val; 13939b9004dSPhilipp Zabel } 14039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_ch_param_read_field); 14139b9004dSPhilipp Zabel 14239b9004dSPhilipp Zabel int ipu_cpmem_set_format_rgb(struct ipu_ch_param __iomem *p, 14339b9004dSPhilipp Zabel const struct ipu_rgb *rgb) 14439b9004dSPhilipp Zabel { 14539b9004dSPhilipp Zabel int bpp = 0, npb = 0, ro, go, bo, to; 14639b9004dSPhilipp Zabel 14739b9004dSPhilipp Zabel ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset; 14839b9004dSPhilipp Zabel go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset; 14939b9004dSPhilipp Zabel bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset; 15039b9004dSPhilipp Zabel to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset; 15139b9004dSPhilipp Zabel 15239b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_WID0, rgb->red.length - 1); 15339b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_OFS0, ro); 15439b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_WID1, rgb->green.length - 1); 15539b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_OFS1, go); 15639b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_WID2, rgb->blue.length - 1); 15739b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_OFS2, bo); 15839b9004dSPhilipp Zabel 15939b9004dSPhilipp Zabel if (rgb->transp.length) { 16039b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_WID3, 16139b9004dSPhilipp Zabel rgb->transp.length - 1); 16239b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_OFS3, to); 16339b9004dSPhilipp Zabel } else { 16439b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_WID3, 7); 16539b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_OFS3, 16639b9004dSPhilipp Zabel rgb->bits_per_pixel); 16739b9004dSPhilipp Zabel } 16839b9004dSPhilipp Zabel 16939b9004dSPhilipp Zabel switch (rgb->bits_per_pixel) { 17039b9004dSPhilipp Zabel case 32: 17139b9004dSPhilipp Zabel bpp = 0; 17239b9004dSPhilipp Zabel npb = 15; 17339b9004dSPhilipp Zabel break; 17439b9004dSPhilipp Zabel case 24: 17539b9004dSPhilipp Zabel bpp = 1; 17639b9004dSPhilipp Zabel npb = 19; 17739b9004dSPhilipp Zabel break; 17839b9004dSPhilipp Zabel case 16: 17939b9004dSPhilipp Zabel bpp = 3; 18039b9004dSPhilipp Zabel npb = 31; 18139b9004dSPhilipp Zabel break; 18239b9004dSPhilipp Zabel case 8: 18339b9004dSPhilipp Zabel bpp = 5; 18439b9004dSPhilipp Zabel npb = 63; 18539b9004dSPhilipp Zabel break; 18639b9004dSPhilipp Zabel default: 18739b9004dSPhilipp Zabel return -EINVAL; 18839b9004dSPhilipp Zabel } 18939b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp); 19039b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb); 19139b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_PFS, 7); /* rgb mode */ 19239b9004dSPhilipp Zabel 19339b9004dSPhilipp Zabel return 0; 19439b9004dSPhilipp Zabel } 19539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb); 19639b9004dSPhilipp Zabel 19739b9004dSPhilipp Zabel int ipu_cpmem_set_format_passthrough(struct ipu_ch_param __iomem *p, 19839b9004dSPhilipp Zabel int width) 19939b9004dSPhilipp Zabel { 20039b9004dSPhilipp Zabel int bpp = 0, npb = 0; 20139b9004dSPhilipp Zabel 20239b9004dSPhilipp Zabel switch (width) { 20339b9004dSPhilipp Zabel case 32: 20439b9004dSPhilipp Zabel bpp = 0; 20539b9004dSPhilipp Zabel npb = 15; 20639b9004dSPhilipp Zabel break; 20739b9004dSPhilipp Zabel case 24: 20839b9004dSPhilipp Zabel bpp = 1; 20939b9004dSPhilipp Zabel npb = 19; 21039b9004dSPhilipp Zabel break; 21139b9004dSPhilipp Zabel case 16: 21239b9004dSPhilipp Zabel bpp = 3; 21339b9004dSPhilipp Zabel npb = 31; 21439b9004dSPhilipp Zabel break; 21539b9004dSPhilipp Zabel case 8: 21639b9004dSPhilipp Zabel bpp = 5; 21739b9004dSPhilipp Zabel npb = 63; 21839b9004dSPhilipp Zabel break; 21939b9004dSPhilipp Zabel default: 22039b9004dSPhilipp Zabel return -EINVAL; 22139b9004dSPhilipp Zabel } 22239b9004dSPhilipp Zabel 22339b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp); 22439b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb); 22539b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_PFS, 6); /* raw mode */ 22639b9004dSPhilipp Zabel 22739b9004dSPhilipp Zabel return 0; 22839b9004dSPhilipp Zabel } 22939b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough); 23039b9004dSPhilipp Zabel 23139b9004dSPhilipp Zabel void ipu_cpmem_set_yuv_interleaved(struct ipu_ch_param __iomem *p, 23239b9004dSPhilipp Zabel u32 pixel_format) 23339b9004dSPhilipp Zabel { 23439b9004dSPhilipp Zabel switch (pixel_format) { 23539b9004dSPhilipp Zabel case V4L2_PIX_FMT_UYVY: 23639b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_BPP, 3); /* bits/pixel */ 23739b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_PFS, 0xA); /* pix format */ 23839b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_NPB, 31); /* burst size */ 23939b9004dSPhilipp Zabel break; 24039b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUYV: 24139b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_BPP, 3); /* bits/pixel */ 24239b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_PFS, 0x8); /* pix format */ 24339b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_NPB, 31); /* burst size */ 24439b9004dSPhilipp Zabel break; 24539b9004dSPhilipp Zabel } 24639b9004dSPhilipp Zabel } 24739b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved); 24839b9004dSPhilipp Zabel 24939b9004dSPhilipp Zabel void ipu_cpmem_set_yuv_planar_full(struct ipu_ch_param __iomem *p, 25039b9004dSPhilipp Zabel u32 pixel_format, int stride, int u_offset, int v_offset) 25139b9004dSPhilipp Zabel { 25239b9004dSPhilipp Zabel switch (pixel_format) { 25339b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUV420: 25439b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_SLUV, (stride / 2) - 1); 25539b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_UBO, u_offset / 8); 25639b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_VBO, v_offset / 8); 25739b9004dSPhilipp Zabel break; 25839b9004dSPhilipp Zabel case V4L2_PIX_FMT_YVU420: 25939b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_SLUV, (stride / 2) - 1); 26039b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_UBO, v_offset / 8); 26139b9004dSPhilipp Zabel ipu_ch_param_write_field(p, IPU_FIELD_VBO, u_offset / 8); 26239b9004dSPhilipp Zabel break; 26339b9004dSPhilipp Zabel } 26439b9004dSPhilipp Zabel } 26539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full); 26639b9004dSPhilipp Zabel 26739b9004dSPhilipp Zabel void ipu_cpmem_set_yuv_planar(struct ipu_ch_param __iomem *p, u32 pixel_format, 26839b9004dSPhilipp Zabel int stride, int height) 26939b9004dSPhilipp Zabel { 27039b9004dSPhilipp Zabel int u_offset, v_offset; 27139b9004dSPhilipp Zabel int uv_stride = 0; 27239b9004dSPhilipp Zabel 27339b9004dSPhilipp Zabel switch (pixel_format) { 27439b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUV420: 27539b9004dSPhilipp Zabel case V4L2_PIX_FMT_YVU420: 27639b9004dSPhilipp Zabel uv_stride = stride / 2; 27739b9004dSPhilipp Zabel u_offset = stride * height; 27839b9004dSPhilipp Zabel v_offset = u_offset + (uv_stride * height / 2); 27939b9004dSPhilipp Zabel ipu_cpmem_set_yuv_planar_full(p, pixel_format, stride, 28039b9004dSPhilipp Zabel u_offset, v_offset); 28139b9004dSPhilipp Zabel break; 28239b9004dSPhilipp Zabel } 28339b9004dSPhilipp Zabel } 28439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar); 28539b9004dSPhilipp Zabel 28639b9004dSPhilipp Zabel static const struct ipu_rgb def_rgb_32 = { 28739b9004dSPhilipp Zabel .red = { .offset = 16, .length = 8, }, 28839b9004dSPhilipp Zabel .green = { .offset = 8, .length = 8, }, 28939b9004dSPhilipp Zabel .blue = { .offset = 0, .length = 8, }, 29039b9004dSPhilipp Zabel .transp = { .offset = 24, .length = 8, }, 29139b9004dSPhilipp Zabel .bits_per_pixel = 32, 29239b9004dSPhilipp Zabel }; 29339b9004dSPhilipp Zabel 29439b9004dSPhilipp Zabel static const struct ipu_rgb def_bgr_32 = { 29539b9004dSPhilipp Zabel .red = { .offset = 0, .length = 8, }, 29639b9004dSPhilipp Zabel .green = { .offset = 8, .length = 8, }, 29739b9004dSPhilipp Zabel .blue = { .offset = 16, .length = 8, }, 29839b9004dSPhilipp Zabel .transp = { .offset = 24, .length = 8, }, 29939b9004dSPhilipp Zabel .bits_per_pixel = 32, 30039b9004dSPhilipp Zabel }; 30139b9004dSPhilipp Zabel 30239b9004dSPhilipp Zabel static const struct ipu_rgb def_rgb_24 = { 30339b9004dSPhilipp Zabel .red = { .offset = 16, .length = 8, }, 30439b9004dSPhilipp Zabel .green = { .offset = 8, .length = 8, }, 30539b9004dSPhilipp Zabel .blue = { .offset = 0, .length = 8, }, 30639b9004dSPhilipp Zabel .transp = { .offset = 0, .length = 0, }, 30739b9004dSPhilipp Zabel .bits_per_pixel = 24, 30839b9004dSPhilipp Zabel }; 30939b9004dSPhilipp Zabel 31039b9004dSPhilipp Zabel static const struct ipu_rgb def_bgr_24 = { 31139b9004dSPhilipp Zabel .red = { .offset = 0, .length = 8, }, 31239b9004dSPhilipp Zabel .green = { .offset = 8, .length = 8, }, 31339b9004dSPhilipp Zabel .blue = { .offset = 16, .length = 8, }, 31439b9004dSPhilipp Zabel .transp = { .offset = 0, .length = 0, }, 31539b9004dSPhilipp Zabel .bits_per_pixel = 24, 31639b9004dSPhilipp Zabel }; 31739b9004dSPhilipp Zabel 31839b9004dSPhilipp Zabel static const struct ipu_rgb def_rgb_16 = { 31939b9004dSPhilipp Zabel .red = { .offset = 11, .length = 5, }, 32039b9004dSPhilipp Zabel .green = { .offset = 5, .length = 6, }, 32139b9004dSPhilipp Zabel .blue = { .offset = 0, .length = 5, }, 32239b9004dSPhilipp Zabel .transp = { .offset = 0, .length = 0, }, 32339b9004dSPhilipp Zabel .bits_per_pixel = 16, 32439b9004dSPhilipp Zabel }; 32539b9004dSPhilipp Zabel 32639b9004dSPhilipp Zabel static const struct ipu_rgb def_bgr_16 = { 32739b9004dSPhilipp Zabel .red = { .offset = 0, .length = 5, }, 32839b9004dSPhilipp Zabel .green = { .offset = 5, .length = 6, }, 32939b9004dSPhilipp Zabel .blue = { .offset = 11, .length = 5, }, 33039b9004dSPhilipp Zabel .transp = { .offset = 0, .length = 0, }, 33139b9004dSPhilipp Zabel .bits_per_pixel = 16, 33239b9004dSPhilipp Zabel }; 33339b9004dSPhilipp Zabel 33439b9004dSPhilipp Zabel #define Y_OFFSET(pix, x, y) ((x) + pix->width * (y)) 33539b9004dSPhilipp Zabel #define U_OFFSET(pix, x, y) ((pix->width * pix->height) + \ 33639b9004dSPhilipp Zabel (pix->width * (y) / 4) + (x) / 2) 33739b9004dSPhilipp Zabel #define V_OFFSET(pix, x, y) ((pix->width * pix->height) + \ 33839b9004dSPhilipp Zabel (pix->width * pix->height / 4) + \ 33939b9004dSPhilipp Zabel (pix->width * (y) / 4) + (x) / 2) 34039b9004dSPhilipp Zabel 34139b9004dSPhilipp Zabel int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 drm_fourcc) 34239b9004dSPhilipp Zabel { 34339b9004dSPhilipp Zabel switch (drm_fourcc) { 34439b9004dSPhilipp Zabel case DRM_FORMAT_YUV420: 34539b9004dSPhilipp Zabel case DRM_FORMAT_YVU420: 34639b9004dSPhilipp Zabel /* pix format */ 34739b9004dSPhilipp Zabel ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 2); 34839b9004dSPhilipp Zabel /* burst size */ 34939b9004dSPhilipp Zabel ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 63); 35039b9004dSPhilipp Zabel break; 35139b9004dSPhilipp Zabel case DRM_FORMAT_UYVY: 35239b9004dSPhilipp Zabel /* bits/pixel */ 35339b9004dSPhilipp Zabel ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3); 35439b9004dSPhilipp Zabel /* pix format */ 35539b9004dSPhilipp Zabel ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 0xA); 35639b9004dSPhilipp Zabel /* burst size */ 35739b9004dSPhilipp Zabel ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31); 35839b9004dSPhilipp Zabel break; 35939b9004dSPhilipp Zabel case DRM_FORMAT_YUYV: 36039b9004dSPhilipp Zabel /* bits/pixel */ 36139b9004dSPhilipp Zabel ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3); 36239b9004dSPhilipp Zabel /* pix format */ 36339b9004dSPhilipp Zabel ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 0x8); 36439b9004dSPhilipp Zabel /* burst size */ 36539b9004dSPhilipp Zabel ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31); 36639b9004dSPhilipp Zabel break; 36739b9004dSPhilipp Zabel case DRM_FORMAT_ABGR8888: 36839b9004dSPhilipp Zabel case DRM_FORMAT_XBGR8888: 36939b9004dSPhilipp Zabel ipu_cpmem_set_format_rgb(cpmem, &def_bgr_32); 37039b9004dSPhilipp Zabel break; 37139b9004dSPhilipp Zabel case DRM_FORMAT_ARGB8888: 37239b9004dSPhilipp Zabel case DRM_FORMAT_XRGB8888: 37339b9004dSPhilipp Zabel ipu_cpmem_set_format_rgb(cpmem, &def_rgb_32); 37439b9004dSPhilipp Zabel break; 37539b9004dSPhilipp Zabel case DRM_FORMAT_BGR888: 37639b9004dSPhilipp Zabel ipu_cpmem_set_format_rgb(cpmem, &def_bgr_24); 37739b9004dSPhilipp Zabel break; 37839b9004dSPhilipp Zabel case DRM_FORMAT_RGB888: 37939b9004dSPhilipp Zabel ipu_cpmem_set_format_rgb(cpmem, &def_rgb_24); 38039b9004dSPhilipp Zabel break; 38139b9004dSPhilipp Zabel case DRM_FORMAT_RGB565: 38239b9004dSPhilipp Zabel ipu_cpmem_set_format_rgb(cpmem, &def_rgb_16); 38339b9004dSPhilipp Zabel break; 38439b9004dSPhilipp Zabel case DRM_FORMAT_BGR565: 38539b9004dSPhilipp Zabel ipu_cpmem_set_format_rgb(cpmem, &def_bgr_16); 38639b9004dSPhilipp Zabel break; 38739b9004dSPhilipp Zabel default: 38839b9004dSPhilipp Zabel return -EINVAL; 38939b9004dSPhilipp Zabel } 39039b9004dSPhilipp Zabel 39139b9004dSPhilipp Zabel return 0; 39239b9004dSPhilipp Zabel } 39339b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt); 39439b9004dSPhilipp Zabel 39539b9004dSPhilipp Zabel /* 39639b9004dSPhilipp Zabel * The V4L2 spec defines packed RGB formats in memory byte order, which from 39739b9004dSPhilipp Zabel * point of view of the IPU corresponds to little-endian words with the first 39839b9004dSPhilipp Zabel * component in the least significant bits. 39939b9004dSPhilipp Zabel * The DRM pixel formats and IPU internal representation are ordered the other 40039b9004dSPhilipp Zabel * way around, with the first named component ordered at the most significant 40139b9004dSPhilipp Zabel * bits. Further, V4L2 formats are not well defined: 40239b9004dSPhilipp Zabel * http://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html 40339b9004dSPhilipp Zabel * We choose the interpretation which matches GStreamer behavior. 40439b9004dSPhilipp Zabel */ 40539b9004dSPhilipp Zabel static int v4l2_pix_fmt_to_drm_fourcc(u32 pixelformat) 40639b9004dSPhilipp Zabel { 40739b9004dSPhilipp Zabel switch (pixelformat) { 40839b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB565: 40939b9004dSPhilipp Zabel /* 41039b9004dSPhilipp Zabel * Here we choose the 'corrected' interpretation of RGBP, a 41139b9004dSPhilipp Zabel * little-endian 16-bit word with the red component at the most 41239b9004dSPhilipp Zabel * significant bits: 41339b9004dSPhilipp Zabel * g[2:0]b[4:0] r[4:0]g[5:3] <=> [16:0] R:G:B 41439b9004dSPhilipp Zabel */ 41539b9004dSPhilipp Zabel return DRM_FORMAT_RGB565; 41639b9004dSPhilipp Zabel case V4L2_PIX_FMT_BGR24: 41739b9004dSPhilipp Zabel /* B G R <=> [24:0] R:G:B */ 41839b9004dSPhilipp Zabel return DRM_FORMAT_RGB888; 41939b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB24: 42039b9004dSPhilipp Zabel /* R G B <=> [24:0] B:G:R */ 42139b9004dSPhilipp Zabel return DRM_FORMAT_BGR888; 42239b9004dSPhilipp Zabel case V4L2_PIX_FMT_BGR32: 42339b9004dSPhilipp Zabel /* B G R A <=> [32:0] A:B:G:R */ 42439b9004dSPhilipp Zabel return DRM_FORMAT_XRGB8888; 42539b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB32: 42639b9004dSPhilipp Zabel /* R G B A <=> [32:0] A:B:G:R */ 42739b9004dSPhilipp Zabel return DRM_FORMAT_XBGR8888; 42839b9004dSPhilipp Zabel case V4L2_PIX_FMT_UYVY: 42939b9004dSPhilipp Zabel return DRM_FORMAT_UYVY; 43039b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUYV: 43139b9004dSPhilipp Zabel return DRM_FORMAT_YUYV; 43239b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUV420: 43339b9004dSPhilipp Zabel return DRM_FORMAT_YUV420; 43439b9004dSPhilipp Zabel case V4L2_PIX_FMT_YVU420: 43539b9004dSPhilipp Zabel return DRM_FORMAT_YVU420; 43639b9004dSPhilipp Zabel } 43739b9004dSPhilipp Zabel 43839b9004dSPhilipp Zabel return -EINVAL; 43939b9004dSPhilipp Zabel } 44039b9004dSPhilipp Zabel 44139b9004dSPhilipp Zabel enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) 44239b9004dSPhilipp Zabel { 44339b9004dSPhilipp Zabel switch (drm_fourcc) { 44439b9004dSPhilipp Zabel case DRM_FORMAT_RGB565: 44539b9004dSPhilipp Zabel case DRM_FORMAT_BGR565: 44639b9004dSPhilipp Zabel case DRM_FORMAT_RGB888: 44739b9004dSPhilipp Zabel case DRM_FORMAT_BGR888: 44839b9004dSPhilipp Zabel case DRM_FORMAT_XRGB8888: 44939b9004dSPhilipp Zabel case DRM_FORMAT_XBGR8888: 45039b9004dSPhilipp Zabel case DRM_FORMAT_RGBX8888: 45139b9004dSPhilipp Zabel case DRM_FORMAT_BGRX8888: 45239b9004dSPhilipp Zabel case DRM_FORMAT_ARGB8888: 45339b9004dSPhilipp Zabel case DRM_FORMAT_ABGR8888: 45439b9004dSPhilipp Zabel case DRM_FORMAT_RGBA8888: 45539b9004dSPhilipp Zabel case DRM_FORMAT_BGRA8888: 45639b9004dSPhilipp Zabel return IPUV3_COLORSPACE_RGB; 45739b9004dSPhilipp Zabel case DRM_FORMAT_YUYV: 45839b9004dSPhilipp Zabel case DRM_FORMAT_UYVY: 45939b9004dSPhilipp Zabel case DRM_FORMAT_YUV420: 46039b9004dSPhilipp Zabel case DRM_FORMAT_YVU420: 46139b9004dSPhilipp Zabel return IPUV3_COLORSPACE_YUV; 46239b9004dSPhilipp Zabel default: 46339b9004dSPhilipp Zabel return IPUV3_COLORSPACE_UNKNOWN; 46439b9004dSPhilipp Zabel } 46539b9004dSPhilipp Zabel } 46639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace); 46739b9004dSPhilipp Zabel 46839b9004dSPhilipp Zabel int ipu_cpmem_set_image(struct ipu_ch_param __iomem *cpmem, 46939b9004dSPhilipp Zabel struct ipu_image *image) 47039b9004dSPhilipp Zabel { 47139b9004dSPhilipp Zabel struct v4l2_pix_format *pix = &image->pix; 47239b9004dSPhilipp Zabel int y_offset, u_offset, v_offset; 47339b9004dSPhilipp Zabel 47439b9004dSPhilipp Zabel pr_debug("%s: resolution: %dx%d stride: %d\n", 47539b9004dSPhilipp Zabel __func__, pix->width, pix->height, 47639b9004dSPhilipp Zabel pix->bytesperline); 47739b9004dSPhilipp Zabel 47839b9004dSPhilipp Zabel ipu_cpmem_set_resolution(cpmem, image->rect.width, 47939b9004dSPhilipp Zabel image->rect.height); 48039b9004dSPhilipp Zabel ipu_cpmem_set_stride(cpmem, pix->bytesperline); 48139b9004dSPhilipp Zabel 48239b9004dSPhilipp Zabel ipu_cpmem_set_fmt(cpmem, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat)); 48339b9004dSPhilipp Zabel 48439b9004dSPhilipp Zabel switch (pix->pixelformat) { 48539b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUV420: 48639b9004dSPhilipp Zabel case V4L2_PIX_FMT_YVU420: 48739b9004dSPhilipp Zabel y_offset = Y_OFFSET(pix, image->rect.left, image->rect.top); 48839b9004dSPhilipp Zabel u_offset = U_OFFSET(pix, image->rect.left, 48939b9004dSPhilipp Zabel image->rect.top) - y_offset; 49039b9004dSPhilipp Zabel v_offset = V_OFFSET(pix, image->rect.left, 49139b9004dSPhilipp Zabel image->rect.top) - y_offset; 49239b9004dSPhilipp Zabel 49339b9004dSPhilipp Zabel ipu_cpmem_set_yuv_planar_full(cpmem, pix->pixelformat, 49439b9004dSPhilipp Zabel pix->bytesperline, u_offset, v_offset); 49539b9004dSPhilipp Zabel ipu_cpmem_set_buffer(cpmem, 0, image->phys + y_offset); 49639b9004dSPhilipp Zabel break; 49739b9004dSPhilipp Zabel case V4L2_PIX_FMT_UYVY: 49839b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUYV: 49939b9004dSPhilipp Zabel ipu_cpmem_set_buffer(cpmem, 0, image->phys + 50039b9004dSPhilipp Zabel image->rect.left * 2 + 50139b9004dSPhilipp Zabel image->rect.top * image->pix.bytesperline); 50239b9004dSPhilipp Zabel break; 50339b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB32: 50439b9004dSPhilipp Zabel case V4L2_PIX_FMT_BGR32: 50539b9004dSPhilipp Zabel ipu_cpmem_set_buffer(cpmem, 0, image->phys + 50639b9004dSPhilipp Zabel image->rect.left * 4 + 50739b9004dSPhilipp Zabel image->rect.top * image->pix.bytesperline); 50839b9004dSPhilipp Zabel break; 50939b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB565: 51039b9004dSPhilipp Zabel ipu_cpmem_set_buffer(cpmem, 0, image->phys + 51139b9004dSPhilipp Zabel image->rect.left * 2 + 51239b9004dSPhilipp Zabel image->rect.top * image->pix.bytesperline); 51339b9004dSPhilipp Zabel break; 51439b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB24: 51539b9004dSPhilipp Zabel case V4L2_PIX_FMT_BGR24: 51639b9004dSPhilipp Zabel ipu_cpmem_set_buffer(cpmem, 0, image->phys + 51739b9004dSPhilipp Zabel image->rect.left * 3 + 51839b9004dSPhilipp Zabel image->rect.top * image->pix.bytesperline); 51939b9004dSPhilipp Zabel break; 52039b9004dSPhilipp Zabel default: 52139b9004dSPhilipp Zabel return -EINVAL; 52239b9004dSPhilipp Zabel } 52339b9004dSPhilipp Zabel 52439b9004dSPhilipp Zabel return 0; 52539b9004dSPhilipp Zabel } 52639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_cpmem_set_image); 52739b9004dSPhilipp Zabel 52839b9004dSPhilipp Zabel enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) 52939b9004dSPhilipp Zabel { 53039b9004dSPhilipp Zabel switch (pixelformat) { 53139b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUV420: 53239b9004dSPhilipp Zabel case V4L2_PIX_FMT_YVU420: 53339b9004dSPhilipp Zabel case V4L2_PIX_FMT_UYVY: 53439b9004dSPhilipp Zabel case V4L2_PIX_FMT_YUYV: 53539b9004dSPhilipp Zabel return IPUV3_COLORSPACE_YUV; 53639b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB32: 53739b9004dSPhilipp Zabel case V4L2_PIX_FMT_BGR32: 53839b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB24: 53939b9004dSPhilipp Zabel case V4L2_PIX_FMT_BGR24: 54039b9004dSPhilipp Zabel case V4L2_PIX_FMT_RGB565: 54139b9004dSPhilipp Zabel return IPUV3_COLORSPACE_RGB; 54239b9004dSPhilipp Zabel default: 54339b9004dSPhilipp Zabel return IPUV3_COLORSPACE_UNKNOWN; 54439b9004dSPhilipp Zabel } 54539b9004dSPhilipp Zabel } 54639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace); 54739b9004dSPhilipp Zabel 54839b9004dSPhilipp Zabel struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) 54939b9004dSPhilipp Zabel { 55039b9004dSPhilipp Zabel struct ipuv3_channel *channel; 55139b9004dSPhilipp Zabel 55239b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, num); 55339b9004dSPhilipp Zabel 55439b9004dSPhilipp Zabel if (num > 63) 55539b9004dSPhilipp Zabel return ERR_PTR(-ENODEV); 55639b9004dSPhilipp Zabel 55739b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock); 55839b9004dSPhilipp Zabel 55939b9004dSPhilipp Zabel channel = &ipu->channel[num]; 56039b9004dSPhilipp Zabel 56139b9004dSPhilipp Zabel if (channel->busy) { 56239b9004dSPhilipp Zabel channel = ERR_PTR(-EBUSY); 56339b9004dSPhilipp Zabel goto out; 56439b9004dSPhilipp Zabel } 56539b9004dSPhilipp Zabel 56639b9004dSPhilipp Zabel channel->busy = true; 56739b9004dSPhilipp Zabel channel->num = num; 56839b9004dSPhilipp Zabel 56939b9004dSPhilipp Zabel out: 57039b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock); 57139b9004dSPhilipp Zabel 57239b9004dSPhilipp Zabel return channel; 57339b9004dSPhilipp Zabel } 57439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_get); 57539b9004dSPhilipp Zabel 57639b9004dSPhilipp Zabel void ipu_idmac_put(struct ipuv3_channel *channel) 57739b9004dSPhilipp Zabel { 57839b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 57939b9004dSPhilipp Zabel 58039b9004dSPhilipp Zabel dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num); 58139b9004dSPhilipp Zabel 58239b9004dSPhilipp Zabel mutex_lock(&ipu->channel_lock); 58339b9004dSPhilipp Zabel 58439b9004dSPhilipp Zabel channel->busy = false; 58539b9004dSPhilipp Zabel 58639b9004dSPhilipp Zabel mutex_unlock(&ipu->channel_lock); 58739b9004dSPhilipp Zabel } 58839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_put); 58939b9004dSPhilipp Zabel 59039b9004dSPhilipp Zabel #define idma_mask(ch) (1 << (ch & 0x1f)) 59139b9004dSPhilipp Zabel 59239b9004dSPhilipp Zabel void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, 59339b9004dSPhilipp Zabel bool doublebuffer) 59439b9004dSPhilipp Zabel { 59539b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 59639b9004dSPhilipp Zabel unsigned long flags; 59739b9004dSPhilipp Zabel u32 reg; 59839b9004dSPhilipp Zabel 59939b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 60039b9004dSPhilipp Zabel 60139b9004dSPhilipp Zabel reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 60239b9004dSPhilipp Zabel if (doublebuffer) 60339b9004dSPhilipp Zabel reg |= idma_mask(channel->num); 60439b9004dSPhilipp Zabel else 60539b9004dSPhilipp Zabel reg &= ~idma_mask(channel->num); 60639b9004dSPhilipp Zabel ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num)); 60739b9004dSPhilipp Zabel 60839b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 60939b9004dSPhilipp Zabel } 61039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer); 61139b9004dSPhilipp Zabel 61239b9004dSPhilipp Zabel int ipu_module_enable(struct ipu_soc *ipu, u32 mask) 61339b9004dSPhilipp Zabel { 61439b9004dSPhilipp Zabel unsigned long lock_flags; 61539b9004dSPhilipp Zabel u32 val; 61639b9004dSPhilipp Zabel 61739b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags); 61839b9004dSPhilipp Zabel 61939b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN); 62039b9004dSPhilipp Zabel 62139b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN) 62239b9004dSPhilipp Zabel val |= IPU_DI0_COUNTER_RELEASE; 62339b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN) 62439b9004dSPhilipp Zabel val |= IPU_DI1_COUNTER_RELEASE; 62539b9004dSPhilipp Zabel 62639b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN); 62739b9004dSPhilipp Zabel 62839b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF); 62939b9004dSPhilipp Zabel val |= mask; 63039b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF); 63139b9004dSPhilipp Zabel 63239b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags); 63339b9004dSPhilipp Zabel 63439b9004dSPhilipp Zabel return 0; 63539b9004dSPhilipp Zabel } 63639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_enable); 63739b9004dSPhilipp Zabel 63839b9004dSPhilipp Zabel int ipu_module_disable(struct ipu_soc *ipu, u32 mask) 63939b9004dSPhilipp Zabel { 64039b9004dSPhilipp Zabel unsigned long lock_flags; 64139b9004dSPhilipp Zabel u32 val; 64239b9004dSPhilipp Zabel 64339b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, lock_flags); 64439b9004dSPhilipp Zabel 64539b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CONF); 64639b9004dSPhilipp Zabel val &= ~mask; 64739b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CONF); 64839b9004dSPhilipp Zabel 64939b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_DISP_GEN); 65039b9004dSPhilipp Zabel 65139b9004dSPhilipp Zabel if (mask & IPU_CONF_DI0_EN) 65239b9004dSPhilipp Zabel val &= ~IPU_DI0_COUNTER_RELEASE; 65339b9004dSPhilipp Zabel if (mask & IPU_CONF_DI1_EN) 65439b9004dSPhilipp Zabel val &= ~IPU_DI1_COUNTER_RELEASE; 65539b9004dSPhilipp Zabel 65639b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_DISP_GEN); 65739b9004dSPhilipp Zabel 65839b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, lock_flags); 65939b9004dSPhilipp Zabel 66039b9004dSPhilipp Zabel return 0; 66139b9004dSPhilipp Zabel } 66239b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_module_disable); 66339b9004dSPhilipp Zabel 66439b9004dSPhilipp Zabel void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num) 66539b9004dSPhilipp Zabel { 66639b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 66739b9004dSPhilipp Zabel unsigned int chno = channel->num; 66839b9004dSPhilipp Zabel unsigned long flags; 66939b9004dSPhilipp Zabel 67039b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 67139b9004dSPhilipp Zabel 67239b9004dSPhilipp Zabel /* Mark buffer as ready. */ 67339b9004dSPhilipp Zabel if (buf_num == 0) 67439b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); 67539b9004dSPhilipp Zabel else 67639b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); 67739b9004dSPhilipp Zabel 67839b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 67939b9004dSPhilipp Zabel } 68039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer); 68139b9004dSPhilipp Zabel 68239b9004dSPhilipp Zabel int ipu_idmac_enable_channel(struct ipuv3_channel *channel) 68339b9004dSPhilipp Zabel { 68439b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 68539b9004dSPhilipp Zabel u32 val; 68639b9004dSPhilipp Zabel unsigned long flags; 68739b9004dSPhilipp Zabel 68839b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 68939b9004dSPhilipp Zabel 69039b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 69139b9004dSPhilipp Zabel val |= idma_mask(channel->num); 69239b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 69339b9004dSPhilipp Zabel 69439b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 69539b9004dSPhilipp Zabel 69639b9004dSPhilipp Zabel return 0; 69739b9004dSPhilipp Zabel } 69839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); 69939b9004dSPhilipp Zabel 70039b9004dSPhilipp Zabel int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) 70139b9004dSPhilipp Zabel { 70239b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 70339b9004dSPhilipp Zabel unsigned long timeout; 70439b9004dSPhilipp Zabel 70539b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(ms); 70639b9004dSPhilipp Zabel while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) & 70739b9004dSPhilipp Zabel idma_mask(channel->num)) { 70839b9004dSPhilipp Zabel if (time_after(jiffies, timeout)) 70939b9004dSPhilipp Zabel return -ETIMEDOUT; 71039b9004dSPhilipp Zabel cpu_relax(); 71139b9004dSPhilipp Zabel } 71239b9004dSPhilipp Zabel 71339b9004dSPhilipp Zabel return 0; 71439b9004dSPhilipp Zabel } 71539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); 71639b9004dSPhilipp Zabel 71739b9004dSPhilipp Zabel int ipu_idmac_disable_channel(struct ipuv3_channel *channel) 71839b9004dSPhilipp Zabel { 71939b9004dSPhilipp Zabel struct ipu_soc *ipu = channel->ipu; 72039b9004dSPhilipp Zabel u32 val; 72139b9004dSPhilipp Zabel unsigned long flags; 72239b9004dSPhilipp Zabel 72339b9004dSPhilipp Zabel spin_lock_irqsave(&ipu->lock, flags); 72439b9004dSPhilipp Zabel 72539b9004dSPhilipp Zabel /* Disable DMA channel(s) */ 72639b9004dSPhilipp Zabel val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); 72739b9004dSPhilipp Zabel val &= ~idma_mask(channel->num); 72839b9004dSPhilipp Zabel ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); 72939b9004dSPhilipp Zabel 73039b9004dSPhilipp Zabel /* Set channel buffers NOT to be ready */ 73139b9004dSPhilipp Zabel ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */ 73239b9004dSPhilipp Zabel 73339b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) & 73439b9004dSPhilipp Zabel idma_mask(channel->num)) { 73539b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num), 73639b9004dSPhilipp Zabel IPU_CHA_BUF0_RDY(channel->num)); 73739b9004dSPhilipp Zabel } 73839b9004dSPhilipp Zabel 73939b9004dSPhilipp Zabel if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) & 74039b9004dSPhilipp Zabel idma_mask(channel->num)) { 74139b9004dSPhilipp Zabel ipu_cm_write(ipu, idma_mask(channel->num), 74239b9004dSPhilipp Zabel IPU_CHA_BUF1_RDY(channel->num)); 74339b9004dSPhilipp Zabel } 74439b9004dSPhilipp Zabel 74539b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ 74639b9004dSPhilipp Zabel 74739b9004dSPhilipp Zabel /* Reset the double buffer */ 74839b9004dSPhilipp Zabel val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); 74939b9004dSPhilipp Zabel val &= ~idma_mask(channel->num); 75039b9004dSPhilipp Zabel ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num)); 75139b9004dSPhilipp Zabel 75239b9004dSPhilipp Zabel spin_unlock_irqrestore(&ipu->lock, flags); 75339b9004dSPhilipp Zabel 75439b9004dSPhilipp Zabel return 0; 75539b9004dSPhilipp Zabel } 75639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel); 75739b9004dSPhilipp Zabel 75839b9004dSPhilipp Zabel static int ipu_memory_reset(struct ipu_soc *ipu) 75939b9004dSPhilipp Zabel { 76039b9004dSPhilipp Zabel unsigned long timeout; 76139b9004dSPhilipp Zabel 76239b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); 76339b9004dSPhilipp Zabel 76439b9004dSPhilipp Zabel timeout = jiffies + msecs_to_jiffies(1000); 76539b9004dSPhilipp Zabel while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { 76639b9004dSPhilipp Zabel if (time_after(jiffies, timeout)) 76739b9004dSPhilipp Zabel return -ETIME; 76839b9004dSPhilipp Zabel cpu_relax(); 76939b9004dSPhilipp Zabel } 77039b9004dSPhilipp Zabel 77139b9004dSPhilipp Zabel return 0; 77239b9004dSPhilipp Zabel } 77339b9004dSPhilipp Zabel 77439b9004dSPhilipp Zabel struct ipu_devtype { 77539b9004dSPhilipp Zabel const char *name; 77639b9004dSPhilipp Zabel unsigned long cm_ofs; 77739b9004dSPhilipp Zabel unsigned long cpmem_ofs; 77839b9004dSPhilipp Zabel unsigned long srm_ofs; 77939b9004dSPhilipp Zabel unsigned long tpm_ofs; 78039b9004dSPhilipp Zabel unsigned long disp0_ofs; 78139b9004dSPhilipp Zabel unsigned long disp1_ofs; 78239b9004dSPhilipp Zabel unsigned long dc_tmpl_ofs; 78339b9004dSPhilipp Zabel unsigned long vdi_ofs; 78439b9004dSPhilipp Zabel enum ipuv3_type type; 78539b9004dSPhilipp Zabel }; 78639b9004dSPhilipp Zabel 78739b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx51 = { 78839b9004dSPhilipp Zabel .name = "IPUv3EX", 78939b9004dSPhilipp Zabel .cm_ofs = 0x1e000000, 79039b9004dSPhilipp Zabel .cpmem_ofs = 0x1f000000, 79139b9004dSPhilipp Zabel .srm_ofs = 0x1f040000, 79239b9004dSPhilipp Zabel .tpm_ofs = 0x1f060000, 79339b9004dSPhilipp Zabel .disp0_ofs = 0x1e040000, 79439b9004dSPhilipp Zabel .disp1_ofs = 0x1e048000, 79539b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x1f080000, 79639b9004dSPhilipp Zabel .vdi_ofs = 0x1e068000, 79739b9004dSPhilipp Zabel .type = IPUV3EX, 79839b9004dSPhilipp Zabel }; 79939b9004dSPhilipp Zabel 80039b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx53 = { 80139b9004dSPhilipp Zabel .name = "IPUv3M", 80239b9004dSPhilipp Zabel .cm_ofs = 0x06000000, 80339b9004dSPhilipp Zabel .cpmem_ofs = 0x07000000, 80439b9004dSPhilipp Zabel .srm_ofs = 0x07040000, 80539b9004dSPhilipp Zabel .tpm_ofs = 0x07060000, 80639b9004dSPhilipp Zabel .disp0_ofs = 0x06040000, 80739b9004dSPhilipp Zabel .disp1_ofs = 0x06048000, 80839b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x07080000, 80939b9004dSPhilipp Zabel .vdi_ofs = 0x06068000, 81039b9004dSPhilipp Zabel .type = IPUV3M, 81139b9004dSPhilipp Zabel }; 81239b9004dSPhilipp Zabel 81339b9004dSPhilipp Zabel static struct ipu_devtype ipu_type_imx6q = { 81439b9004dSPhilipp Zabel .name = "IPUv3H", 81539b9004dSPhilipp Zabel .cm_ofs = 0x00200000, 81639b9004dSPhilipp Zabel .cpmem_ofs = 0x00300000, 81739b9004dSPhilipp Zabel .srm_ofs = 0x00340000, 81839b9004dSPhilipp Zabel .tpm_ofs = 0x00360000, 81939b9004dSPhilipp Zabel .disp0_ofs = 0x00240000, 82039b9004dSPhilipp Zabel .disp1_ofs = 0x00248000, 82139b9004dSPhilipp Zabel .dc_tmpl_ofs = 0x00380000, 82239b9004dSPhilipp Zabel .vdi_ofs = 0x00268000, 82339b9004dSPhilipp Zabel .type = IPUV3H, 82439b9004dSPhilipp Zabel }; 82539b9004dSPhilipp Zabel 82639b9004dSPhilipp Zabel static const struct of_device_id imx_ipu_dt_ids[] = { 82739b9004dSPhilipp Zabel { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, 82839b9004dSPhilipp Zabel { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, 82939b9004dSPhilipp Zabel { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, 83039b9004dSPhilipp Zabel { /* sentinel */ } 83139b9004dSPhilipp Zabel }; 83239b9004dSPhilipp Zabel MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); 83339b9004dSPhilipp Zabel 83439b9004dSPhilipp Zabel static int ipu_submodules_init(struct ipu_soc *ipu, 83539b9004dSPhilipp Zabel struct platform_device *pdev, unsigned long ipu_base, 83639b9004dSPhilipp Zabel struct clk *ipu_clk) 83739b9004dSPhilipp Zabel { 83839b9004dSPhilipp Zabel char *unit; 83939b9004dSPhilipp Zabel int ret; 84039b9004dSPhilipp Zabel struct device *dev = &pdev->dev; 84139b9004dSPhilipp Zabel const struct ipu_devtype *devtype = ipu->devtype; 84239b9004dSPhilipp Zabel 84339b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, 84439b9004dSPhilipp Zabel IPU_CONF_DI0_EN, ipu_clk); 84539b9004dSPhilipp Zabel if (ret) { 84639b9004dSPhilipp Zabel unit = "di0"; 84739b9004dSPhilipp Zabel goto err_di_0; 84839b9004dSPhilipp Zabel } 84939b9004dSPhilipp Zabel 85039b9004dSPhilipp Zabel ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs, 85139b9004dSPhilipp Zabel IPU_CONF_DI1_EN, ipu_clk); 85239b9004dSPhilipp Zabel if (ret) { 85339b9004dSPhilipp Zabel unit = "di1"; 85439b9004dSPhilipp Zabel goto err_di_1; 85539b9004dSPhilipp Zabel } 85639b9004dSPhilipp Zabel 85739b9004dSPhilipp Zabel ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs + 85839b9004dSPhilipp Zabel IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs); 85939b9004dSPhilipp Zabel if (ret) { 86039b9004dSPhilipp Zabel unit = "dc_template"; 86139b9004dSPhilipp Zabel goto err_dc; 86239b9004dSPhilipp Zabel } 86339b9004dSPhilipp Zabel 86439b9004dSPhilipp Zabel ret = ipu_dmfc_init(ipu, dev, ipu_base + 86539b9004dSPhilipp Zabel devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk); 86639b9004dSPhilipp Zabel if (ret) { 86739b9004dSPhilipp Zabel unit = "dmfc"; 86839b9004dSPhilipp Zabel goto err_dmfc; 86939b9004dSPhilipp Zabel } 87039b9004dSPhilipp Zabel 87139b9004dSPhilipp Zabel ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs); 87239b9004dSPhilipp Zabel if (ret) { 87339b9004dSPhilipp Zabel unit = "dp"; 87439b9004dSPhilipp Zabel goto err_dp; 87539b9004dSPhilipp Zabel } 87639b9004dSPhilipp Zabel 87739b9004dSPhilipp Zabel return 0; 87839b9004dSPhilipp Zabel 87939b9004dSPhilipp Zabel err_dp: 88039b9004dSPhilipp Zabel ipu_dmfc_exit(ipu); 88139b9004dSPhilipp Zabel err_dmfc: 88239b9004dSPhilipp Zabel ipu_dc_exit(ipu); 88339b9004dSPhilipp Zabel err_dc: 88439b9004dSPhilipp Zabel ipu_di_exit(ipu, 1); 88539b9004dSPhilipp Zabel err_di_1: 88639b9004dSPhilipp Zabel ipu_di_exit(ipu, 0); 88739b9004dSPhilipp Zabel err_di_0: 88839b9004dSPhilipp Zabel dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); 88939b9004dSPhilipp Zabel return ret; 89039b9004dSPhilipp Zabel } 89139b9004dSPhilipp Zabel 89239b9004dSPhilipp Zabel static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) 89339b9004dSPhilipp Zabel { 89439b9004dSPhilipp Zabel unsigned long status; 89539b9004dSPhilipp Zabel int i, bit, irq; 89639b9004dSPhilipp Zabel 89739b9004dSPhilipp Zabel for (i = 0; i < num_regs; i++) { 89839b9004dSPhilipp Zabel 89939b9004dSPhilipp Zabel status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i])); 90039b9004dSPhilipp Zabel status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i])); 90139b9004dSPhilipp Zabel 90239b9004dSPhilipp Zabel for_each_set_bit(bit, &status, 32) { 90339b9004dSPhilipp Zabel irq = irq_linear_revmap(ipu->domain, regs[i] * 32 + bit); 90439b9004dSPhilipp Zabel if (irq) 90539b9004dSPhilipp Zabel generic_handle_irq(irq); 90639b9004dSPhilipp Zabel } 90739b9004dSPhilipp Zabel } 90839b9004dSPhilipp Zabel } 90939b9004dSPhilipp Zabel 91039b9004dSPhilipp Zabel static void ipu_irq_handler(unsigned int irq, struct irq_desc *desc) 91139b9004dSPhilipp Zabel { 91239b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 91339b9004dSPhilipp Zabel const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14}; 91439b9004dSPhilipp Zabel struct irq_chip *chip = irq_get_chip(irq); 91539b9004dSPhilipp Zabel 91639b9004dSPhilipp Zabel chained_irq_enter(chip, desc); 91739b9004dSPhilipp Zabel 91839b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 91939b9004dSPhilipp Zabel 92039b9004dSPhilipp Zabel chained_irq_exit(chip, desc); 92139b9004dSPhilipp Zabel } 92239b9004dSPhilipp Zabel 92339b9004dSPhilipp Zabel static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc) 92439b9004dSPhilipp Zabel { 92539b9004dSPhilipp Zabel struct ipu_soc *ipu = irq_desc_get_handler_data(desc); 92639b9004dSPhilipp Zabel const int int_reg[] = { 4, 5, 8, 9}; 92739b9004dSPhilipp Zabel struct irq_chip *chip = irq_get_chip(irq); 92839b9004dSPhilipp Zabel 92939b9004dSPhilipp Zabel chained_irq_enter(chip, desc); 93039b9004dSPhilipp Zabel 93139b9004dSPhilipp Zabel ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); 93239b9004dSPhilipp Zabel 93339b9004dSPhilipp Zabel chained_irq_exit(chip, desc); 93439b9004dSPhilipp Zabel } 93539b9004dSPhilipp Zabel 93639b9004dSPhilipp Zabel int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, 93739b9004dSPhilipp Zabel enum ipu_channel_irq irq_type) 93839b9004dSPhilipp Zabel { 93939b9004dSPhilipp Zabel int irq = irq_linear_revmap(ipu->domain, irq_type + channel->num); 94039b9004dSPhilipp Zabel 94139b9004dSPhilipp Zabel if (!irq) 94239b9004dSPhilipp Zabel irq = irq_create_mapping(ipu->domain, irq_type + channel->num); 94339b9004dSPhilipp Zabel 94439b9004dSPhilipp Zabel return irq; 94539b9004dSPhilipp Zabel } 94639b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); 94739b9004dSPhilipp Zabel 94839b9004dSPhilipp Zabel static void ipu_submodules_exit(struct ipu_soc *ipu) 94939b9004dSPhilipp Zabel { 95039b9004dSPhilipp Zabel ipu_dp_exit(ipu); 95139b9004dSPhilipp Zabel ipu_dmfc_exit(ipu); 95239b9004dSPhilipp Zabel ipu_dc_exit(ipu); 95339b9004dSPhilipp Zabel ipu_di_exit(ipu, 1); 95439b9004dSPhilipp Zabel ipu_di_exit(ipu, 0); 95539b9004dSPhilipp Zabel } 95639b9004dSPhilipp Zabel 95739b9004dSPhilipp Zabel static int platform_remove_devices_fn(struct device *dev, void *unused) 95839b9004dSPhilipp Zabel { 95939b9004dSPhilipp Zabel struct platform_device *pdev = to_platform_device(dev); 96039b9004dSPhilipp Zabel 96139b9004dSPhilipp Zabel platform_device_unregister(pdev); 96239b9004dSPhilipp Zabel 96339b9004dSPhilipp Zabel return 0; 96439b9004dSPhilipp Zabel } 96539b9004dSPhilipp Zabel 96639b9004dSPhilipp Zabel static void platform_device_unregister_children(struct platform_device *pdev) 96739b9004dSPhilipp Zabel { 96839b9004dSPhilipp Zabel device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn); 96939b9004dSPhilipp Zabel } 97039b9004dSPhilipp Zabel 97139b9004dSPhilipp Zabel struct ipu_platform_reg { 97239b9004dSPhilipp Zabel struct ipu_client_platformdata pdata; 97339b9004dSPhilipp Zabel const char *name; 97439b9004dSPhilipp Zabel }; 97539b9004dSPhilipp Zabel 97639b9004dSPhilipp Zabel static const struct ipu_platform_reg client_reg[] = { 97739b9004dSPhilipp Zabel { 97839b9004dSPhilipp Zabel .pdata = { 97939b9004dSPhilipp Zabel .di = 0, 98039b9004dSPhilipp Zabel .dc = 5, 98139b9004dSPhilipp Zabel .dp = IPU_DP_FLOW_SYNC_BG, 98239b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC, 98339b9004dSPhilipp Zabel .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC, 98439b9004dSPhilipp Zabel }, 98539b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc", 98639b9004dSPhilipp Zabel }, { 98739b9004dSPhilipp Zabel .pdata = { 98839b9004dSPhilipp Zabel .di = 1, 98939b9004dSPhilipp Zabel .dc = 1, 99039b9004dSPhilipp Zabel .dp = -EINVAL, 99139b9004dSPhilipp Zabel .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC, 99239b9004dSPhilipp Zabel .dma[1] = -EINVAL, 99339b9004dSPhilipp Zabel }, 99439b9004dSPhilipp Zabel .name = "imx-ipuv3-crtc", 99539b9004dSPhilipp Zabel }, 99639b9004dSPhilipp Zabel }; 99739b9004dSPhilipp Zabel 99839b9004dSPhilipp Zabel static DEFINE_MUTEX(ipu_client_id_mutex); 99939b9004dSPhilipp Zabel static int ipu_client_id; 100039b9004dSPhilipp Zabel 100139b9004dSPhilipp Zabel static int ipu_add_client_devices(struct ipu_soc *ipu) 100239b9004dSPhilipp Zabel { 100339b9004dSPhilipp Zabel struct device *dev = ipu->dev; 100439b9004dSPhilipp Zabel unsigned i; 100539b9004dSPhilipp Zabel int id, ret; 100639b9004dSPhilipp Zabel 100739b9004dSPhilipp Zabel mutex_lock(&ipu_client_id_mutex); 100839b9004dSPhilipp Zabel id = ipu_client_id; 100939b9004dSPhilipp Zabel ipu_client_id += ARRAY_SIZE(client_reg); 101039b9004dSPhilipp Zabel mutex_unlock(&ipu_client_id_mutex); 101139b9004dSPhilipp Zabel 101239b9004dSPhilipp Zabel for (i = 0; i < ARRAY_SIZE(client_reg); i++) { 101339b9004dSPhilipp Zabel const struct ipu_platform_reg *reg = &client_reg[i]; 101439b9004dSPhilipp Zabel struct platform_device *pdev; 101539b9004dSPhilipp Zabel 101639b9004dSPhilipp Zabel pdev = platform_device_register_data(dev, reg->name, 101739b9004dSPhilipp Zabel id++, ®->pdata, sizeof(reg->pdata)); 101839b9004dSPhilipp Zabel 101939b9004dSPhilipp Zabel if (IS_ERR(pdev)) 102039b9004dSPhilipp Zabel goto err_register; 102139b9004dSPhilipp Zabel } 102239b9004dSPhilipp Zabel 102339b9004dSPhilipp Zabel return 0; 102439b9004dSPhilipp Zabel 102539b9004dSPhilipp Zabel err_register: 102639b9004dSPhilipp Zabel platform_device_unregister_children(to_platform_device(dev)); 102739b9004dSPhilipp Zabel 102839b9004dSPhilipp Zabel return ret; 102939b9004dSPhilipp Zabel } 103039b9004dSPhilipp Zabel 103139b9004dSPhilipp Zabel 103239b9004dSPhilipp Zabel static int ipu_irq_init(struct ipu_soc *ipu) 103339b9004dSPhilipp Zabel { 103439b9004dSPhilipp Zabel struct irq_chip_generic *gc; 103539b9004dSPhilipp Zabel struct irq_chip_type *ct; 103639b9004dSPhilipp Zabel unsigned long unused[IPU_NUM_IRQS / 32] = { 103739b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 103839b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 103939b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 104039b9004dSPhilipp Zabel 0x4077ffff, 0xffe7e1fd, 104139b9004dSPhilipp Zabel 0x23fffffe, 0x8880fff0, 104239b9004dSPhilipp Zabel 0xf98fe7d0, 0xfff81fff, 104339b9004dSPhilipp Zabel 0x400100d0, 0xffe000fd, 104439b9004dSPhilipp Zabel 0x00000000, 104539b9004dSPhilipp Zabel }; 104639b9004dSPhilipp Zabel int ret, i; 104739b9004dSPhilipp Zabel 104839b9004dSPhilipp Zabel ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS, 104939b9004dSPhilipp Zabel &irq_generic_chip_ops, ipu); 105039b9004dSPhilipp Zabel if (!ipu->domain) { 105139b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to add irq domain\n"); 105239b9004dSPhilipp Zabel return -ENODEV; 105339b9004dSPhilipp Zabel } 105439b9004dSPhilipp Zabel 105539b9004dSPhilipp Zabel ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", 105639b9004dSPhilipp Zabel handle_level_irq, 0, IRQF_VALID, 0); 105739b9004dSPhilipp Zabel if (ret < 0) { 105839b9004dSPhilipp Zabel dev_err(ipu->dev, "failed to alloc generic irq chips\n"); 105939b9004dSPhilipp Zabel irq_domain_remove(ipu->domain); 106039b9004dSPhilipp Zabel return ret; 106139b9004dSPhilipp Zabel } 106239b9004dSPhilipp Zabel 106339b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i += 32) { 106439b9004dSPhilipp Zabel gc = irq_get_domain_generic_chip(ipu->domain, i); 106539b9004dSPhilipp Zabel gc->reg_base = ipu->cm_reg; 106639b9004dSPhilipp Zabel gc->unused = unused[i / 32]; 106739b9004dSPhilipp Zabel ct = gc->chip_types; 106839b9004dSPhilipp Zabel ct->chip.irq_ack = irq_gc_ack_set_bit; 106939b9004dSPhilipp Zabel ct->chip.irq_mask = irq_gc_mask_clr_bit; 107039b9004dSPhilipp Zabel ct->chip.irq_unmask = irq_gc_mask_set_bit; 107139b9004dSPhilipp Zabel ct->regs.ack = IPU_INT_STAT(i / 32); 107239b9004dSPhilipp Zabel ct->regs.mask = IPU_INT_CTRL(i / 32); 107339b9004dSPhilipp Zabel } 107439b9004dSPhilipp Zabel 107539b9004dSPhilipp Zabel irq_set_chained_handler(ipu->irq_sync, ipu_irq_handler); 107639b9004dSPhilipp Zabel irq_set_handler_data(ipu->irq_sync, ipu); 107739b9004dSPhilipp Zabel irq_set_chained_handler(ipu->irq_err, ipu_err_irq_handler); 107839b9004dSPhilipp Zabel irq_set_handler_data(ipu->irq_err, ipu); 107939b9004dSPhilipp Zabel 108039b9004dSPhilipp Zabel return 0; 108139b9004dSPhilipp Zabel } 108239b9004dSPhilipp Zabel 108339b9004dSPhilipp Zabel static void ipu_irq_exit(struct ipu_soc *ipu) 108439b9004dSPhilipp Zabel { 108539b9004dSPhilipp Zabel int i, irq; 108639b9004dSPhilipp Zabel 108739b9004dSPhilipp Zabel irq_set_chained_handler(ipu->irq_err, NULL); 108839b9004dSPhilipp Zabel irq_set_handler_data(ipu->irq_err, NULL); 108939b9004dSPhilipp Zabel irq_set_chained_handler(ipu->irq_sync, NULL); 109039b9004dSPhilipp Zabel irq_set_handler_data(ipu->irq_sync, NULL); 109139b9004dSPhilipp Zabel 109239b9004dSPhilipp Zabel /* TODO: remove irq_domain_generic_chips */ 109339b9004dSPhilipp Zabel 109439b9004dSPhilipp Zabel for (i = 0; i < IPU_NUM_IRQS; i++) { 109539b9004dSPhilipp Zabel irq = irq_linear_revmap(ipu->domain, i); 109639b9004dSPhilipp Zabel if (irq) 109739b9004dSPhilipp Zabel irq_dispose_mapping(irq); 109839b9004dSPhilipp Zabel } 109939b9004dSPhilipp Zabel 110039b9004dSPhilipp Zabel irq_domain_remove(ipu->domain); 110139b9004dSPhilipp Zabel } 110239b9004dSPhilipp Zabel 110339b9004dSPhilipp Zabel static int ipu_probe(struct platform_device *pdev) 110439b9004dSPhilipp Zabel { 110539b9004dSPhilipp Zabel const struct of_device_id *of_id = 110639b9004dSPhilipp Zabel of_match_device(imx_ipu_dt_ids, &pdev->dev); 110739b9004dSPhilipp Zabel struct ipu_soc *ipu; 110839b9004dSPhilipp Zabel struct resource *res; 110939b9004dSPhilipp Zabel unsigned long ipu_base; 111039b9004dSPhilipp Zabel int i, ret, irq_sync, irq_err; 111139b9004dSPhilipp Zabel const struct ipu_devtype *devtype; 111239b9004dSPhilipp Zabel 111339b9004dSPhilipp Zabel devtype = of_id->data; 111439b9004dSPhilipp Zabel 111539b9004dSPhilipp Zabel irq_sync = platform_get_irq(pdev, 0); 111639b9004dSPhilipp Zabel irq_err = platform_get_irq(pdev, 1); 111739b9004dSPhilipp Zabel res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 111839b9004dSPhilipp Zabel 111939b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n", 112039b9004dSPhilipp Zabel irq_sync, irq_err); 112139b9004dSPhilipp Zabel 112239b9004dSPhilipp Zabel if (!res || irq_sync < 0 || irq_err < 0) 112339b9004dSPhilipp Zabel return -ENODEV; 112439b9004dSPhilipp Zabel 112539b9004dSPhilipp Zabel ipu_base = res->start; 112639b9004dSPhilipp Zabel 112739b9004dSPhilipp Zabel ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL); 112839b9004dSPhilipp Zabel if (!ipu) 112939b9004dSPhilipp Zabel return -ENODEV; 113039b9004dSPhilipp Zabel 113139b9004dSPhilipp Zabel for (i = 0; i < 64; i++) 113239b9004dSPhilipp Zabel ipu->channel[i].ipu = ipu; 113339b9004dSPhilipp Zabel ipu->devtype = devtype; 113439b9004dSPhilipp Zabel ipu->ipu_type = devtype->type; 113539b9004dSPhilipp Zabel 113639b9004dSPhilipp Zabel spin_lock_init(&ipu->lock); 113739b9004dSPhilipp Zabel mutex_init(&ipu->channel_lock); 113839b9004dSPhilipp Zabel 113939b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n", 114039b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs); 114139b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "idmac: 0x%08lx\n", 114239b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS); 114339b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n", 114439b9004dSPhilipp Zabel ipu_base + devtype->cpmem_ofs); 114539b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp0: 0x%08lx\n", 114639b9004dSPhilipp Zabel ipu_base + devtype->disp0_ofs); 114739b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "disp1: 0x%08lx\n", 114839b9004dSPhilipp Zabel ipu_base + devtype->disp1_ofs); 114939b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "srm: 0x%08lx\n", 115039b9004dSPhilipp Zabel ipu_base + devtype->srm_ofs); 115139b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "tpm: 0x%08lx\n", 115239b9004dSPhilipp Zabel ipu_base + devtype->tpm_ofs); 115339b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dc: 0x%08lx\n", 115439b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS); 115539b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "ic: 0x%08lx\n", 115639b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS); 115739b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n", 115839b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS); 115939b9004dSPhilipp Zabel dev_dbg(&pdev->dev, "vdi: 0x%08lx\n", 116039b9004dSPhilipp Zabel ipu_base + devtype->vdi_ofs); 116139b9004dSPhilipp Zabel 116239b9004dSPhilipp Zabel ipu->cm_reg = devm_ioremap(&pdev->dev, 116339b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs, PAGE_SIZE); 116439b9004dSPhilipp Zabel ipu->idmac_reg = devm_ioremap(&pdev->dev, 116539b9004dSPhilipp Zabel ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, 116639b9004dSPhilipp Zabel PAGE_SIZE); 116739b9004dSPhilipp Zabel ipu->cpmem_base = devm_ioremap(&pdev->dev, 116839b9004dSPhilipp Zabel ipu_base + devtype->cpmem_ofs, PAGE_SIZE); 116939b9004dSPhilipp Zabel 117039b9004dSPhilipp Zabel if (!ipu->cm_reg || !ipu->idmac_reg || !ipu->cpmem_base) 117139b9004dSPhilipp Zabel return -ENOMEM; 117239b9004dSPhilipp Zabel 117339b9004dSPhilipp Zabel ipu->clk = devm_clk_get(&pdev->dev, "bus"); 117439b9004dSPhilipp Zabel if (IS_ERR(ipu->clk)) { 117539b9004dSPhilipp Zabel ret = PTR_ERR(ipu->clk); 117639b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_get failed with %d", ret); 117739b9004dSPhilipp Zabel return ret; 117839b9004dSPhilipp Zabel } 117939b9004dSPhilipp Zabel 118039b9004dSPhilipp Zabel platform_set_drvdata(pdev, ipu); 118139b9004dSPhilipp Zabel 118239b9004dSPhilipp Zabel ret = clk_prepare_enable(ipu->clk); 118339b9004dSPhilipp Zabel if (ret) { 118439b9004dSPhilipp Zabel dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); 118539b9004dSPhilipp Zabel return ret; 118639b9004dSPhilipp Zabel } 118739b9004dSPhilipp Zabel 118839b9004dSPhilipp Zabel ipu->dev = &pdev->dev; 118939b9004dSPhilipp Zabel ipu->irq_sync = irq_sync; 119039b9004dSPhilipp Zabel ipu->irq_err = irq_err; 119139b9004dSPhilipp Zabel 119239b9004dSPhilipp Zabel ret = ipu_irq_init(ipu); 119339b9004dSPhilipp Zabel if (ret) 119439b9004dSPhilipp Zabel goto out_failed_irq; 119539b9004dSPhilipp Zabel 119639b9004dSPhilipp Zabel ret = device_reset(&pdev->dev); 119739b9004dSPhilipp Zabel if (ret) { 119839b9004dSPhilipp Zabel dev_err(&pdev->dev, "failed to reset: %d\n", ret); 119939b9004dSPhilipp Zabel goto out_failed_reset; 120039b9004dSPhilipp Zabel } 120139b9004dSPhilipp Zabel ret = ipu_memory_reset(ipu); 120239b9004dSPhilipp Zabel if (ret) 120339b9004dSPhilipp Zabel goto out_failed_reset; 120439b9004dSPhilipp Zabel 120539b9004dSPhilipp Zabel /* Set MCU_T to divide MCU access window into 2 */ 120639b9004dSPhilipp Zabel ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), 120739b9004dSPhilipp Zabel IPU_DISP_GEN); 120839b9004dSPhilipp Zabel 120939b9004dSPhilipp Zabel ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk); 121039b9004dSPhilipp Zabel if (ret) 121139b9004dSPhilipp Zabel goto failed_submodules_init; 121239b9004dSPhilipp Zabel 121339b9004dSPhilipp Zabel ret = ipu_add_client_devices(ipu); 121439b9004dSPhilipp Zabel if (ret) { 121539b9004dSPhilipp Zabel dev_err(&pdev->dev, "adding client devices failed with %d\n", 121639b9004dSPhilipp Zabel ret); 121739b9004dSPhilipp Zabel goto failed_add_clients; 121839b9004dSPhilipp Zabel } 121939b9004dSPhilipp Zabel 122039b9004dSPhilipp Zabel dev_info(&pdev->dev, "%s probed\n", devtype->name); 122139b9004dSPhilipp Zabel 122239b9004dSPhilipp Zabel return 0; 122339b9004dSPhilipp Zabel 122439b9004dSPhilipp Zabel failed_add_clients: 122539b9004dSPhilipp Zabel ipu_submodules_exit(ipu); 122639b9004dSPhilipp Zabel failed_submodules_init: 122739b9004dSPhilipp Zabel out_failed_reset: 122839b9004dSPhilipp Zabel ipu_irq_exit(ipu); 122939b9004dSPhilipp Zabel out_failed_irq: 123039b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk); 123139b9004dSPhilipp Zabel return ret; 123239b9004dSPhilipp Zabel } 123339b9004dSPhilipp Zabel 123439b9004dSPhilipp Zabel static int ipu_remove(struct platform_device *pdev) 123539b9004dSPhilipp Zabel { 123639b9004dSPhilipp Zabel struct ipu_soc *ipu = platform_get_drvdata(pdev); 123739b9004dSPhilipp Zabel 123839b9004dSPhilipp Zabel platform_device_unregister_children(pdev); 123939b9004dSPhilipp Zabel ipu_submodules_exit(ipu); 124039b9004dSPhilipp Zabel ipu_irq_exit(ipu); 124139b9004dSPhilipp Zabel 124239b9004dSPhilipp Zabel clk_disable_unprepare(ipu->clk); 124339b9004dSPhilipp Zabel 124439b9004dSPhilipp Zabel return 0; 124539b9004dSPhilipp Zabel } 124639b9004dSPhilipp Zabel 124739b9004dSPhilipp Zabel static struct platform_driver imx_ipu_driver = { 124839b9004dSPhilipp Zabel .driver = { 124939b9004dSPhilipp Zabel .name = "imx-ipuv3", 125039b9004dSPhilipp Zabel .of_match_table = imx_ipu_dt_ids, 125139b9004dSPhilipp Zabel }, 125239b9004dSPhilipp Zabel .probe = ipu_probe, 125339b9004dSPhilipp Zabel .remove = ipu_remove, 125439b9004dSPhilipp Zabel }; 125539b9004dSPhilipp Zabel 125639b9004dSPhilipp Zabel module_platform_driver(imx_ipu_driver); 125739b9004dSPhilipp Zabel 125839b9004dSPhilipp Zabel MODULE_ALIAS("platform:imx-ipuv3"); 125939b9004dSPhilipp Zabel MODULE_DESCRIPTION("i.MX IPU v3 driver"); 126039b9004dSPhilipp Zabel MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 126139b9004dSPhilipp Zabel MODULE_LICENSE("GPL"); 1262