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/export.h> 1639b9004dSPhilipp Zabel #include <linux/types.h> 1739b9004dSPhilipp Zabel #include <linux/errno.h> 1839b9004dSPhilipp Zabel #include <linux/io.h> 1939b9004dSPhilipp Zabel 2039b9004dSPhilipp Zabel #include <video/imx-ipu-v3.h> 2139b9004dSPhilipp Zabel #include "ipu-prv.h" 2239b9004dSPhilipp Zabel 2339b9004dSPhilipp Zabel #define DMFC_RD_CHAN 0x0000 2439b9004dSPhilipp Zabel #define DMFC_WR_CHAN 0x0004 2539b9004dSPhilipp Zabel #define DMFC_WR_CHAN_DEF 0x0008 2639b9004dSPhilipp Zabel #define DMFC_DP_CHAN 0x000c 2739b9004dSPhilipp Zabel #define DMFC_DP_CHAN_DEF 0x0010 2839b9004dSPhilipp Zabel #define DMFC_GENERAL1 0x0014 2939b9004dSPhilipp Zabel #define DMFC_GENERAL2 0x0018 3039b9004dSPhilipp Zabel #define DMFC_IC_CTRL 0x001c 31682b7c1cSLinus Torvalds #define DMFC_WR_CHAN_ALT 0x0020 32682b7c1cSLinus Torvalds #define DMFC_WR_CHAN_DEF_ALT 0x0024 33682b7c1cSLinus Torvalds #define DMFC_DP_CHAN_ALT 0x0028 34682b7c1cSLinus Torvalds #define DMFC_DP_CHAN_DEF_ALT 0x002c 35682b7c1cSLinus Torvalds #define DMFC_GENERAL1_ALT 0x0030 36682b7c1cSLinus Torvalds #define DMFC_STAT 0x0034 3739b9004dSPhilipp Zabel 3839b9004dSPhilipp Zabel #define DMFC_WR_CHAN_1_28 0 3939b9004dSPhilipp Zabel #define DMFC_WR_CHAN_2_41 8 4039b9004dSPhilipp Zabel #define DMFC_WR_CHAN_1C_42 16 4139b9004dSPhilipp Zabel #define DMFC_WR_CHAN_2C_43 24 4239b9004dSPhilipp Zabel 4339b9004dSPhilipp Zabel #define DMFC_DP_CHAN_5B_23 0 4439b9004dSPhilipp Zabel #define DMFC_DP_CHAN_5F_27 8 4539b9004dSPhilipp Zabel #define DMFC_DP_CHAN_6B_24 16 4639b9004dSPhilipp Zabel #define DMFC_DP_CHAN_6F_29 24 4739b9004dSPhilipp Zabel 4839b9004dSPhilipp Zabel struct dmfc_channel_data { 4939b9004dSPhilipp Zabel int ipu_channel; 5039b9004dSPhilipp Zabel unsigned long channel_reg; 5139b9004dSPhilipp Zabel unsigned long shift; 5239b9004dSPhilipp Zabel unsigned eot_shift; 5339b9004dSPhilipp Zabel unsigned max_fifo_lines; 5439b9004dSPhilipp Zabel }; 5539b9004dSPhilipp Zabel 5639b9004dSPhilipp Zabel static const struct dmfc_channel_data dmfcdata[] = { 5739b9004dSPhilipp Zabel { 5839b9004dSPhilipp Zabel .ipu_channel = IPUV3_CHANNEL_MEM_BG_SYNC, 5939b9004dSPhilipp Zabel .channel_reg = DMFC_DP_CHAN, 6039b9004dSPhilipp Zabel .shift = DMFC_DP_CHAN_5B_23, 6139b9004dSPhilipp Zabel .eot_shift = 20, 6239b9004dSPhilipp Zabel .max_fifo_lines = 3, 6339b9004dSPhilipp Zabel }, { 6439b9004dSPhilipp Zabel .ipu_channel = 24, 6539b9004dSPhilipp Zabel .channel_reg = DMFC_DP_CHAN, 6639b9004dSPhilipp Zabel .shift = DMFC_DP_CHAN_6B_24, 6739b9004dSPhilipp Zabel .eot_shift = 22, 6839b9004dSPhilipp Zabel .max_fifo_lines = 1, 6939b9004dSPhilipp Zabel }, { 7039b9004dSPhilipp Zabel .ipu_channel = IPUV3_CHANNEL_MEM_FG_SYNC, 7139b9004dSPhilipp Zabel .channel_reg = DMFC_DP_CHAN, 7239b9004dSPhilipp Zabel .shift = DMFC_DP_CHAN_5F_27, 7339b9004dSPhilipp Zabel .eot_shift = 21, 7439b9004dSPhilipp Zabel .max_fifo_lines = 2, 7539b9004dSPhilipp Zabel }, { 7639b9004dSPhilipp Zabel .ipu_channel = IPUV3_CHANNEL_MEM_DC_SYNC, 7739b9004dSPhilipp Zabel .channel_reg = DMFC_WR_CHAN, 7839b9004dSPhilipp Zabel .shift = DMFC_WR_CHAN_1_28, 7939b9004dSPhilipp Zabel .eot_shift = 16, 8039b9004dSPhilipp Zabel .max_fifo_lines = 2, 8139b9004dSPhilipp Zabel }, { 8239b9004dSPhilipp Zabel .ipu_channel = 29, 8339b9004dSPhilipp Zabel .channel_reg = DMFC_DP_CHAN, 8439b9004dSPhilipp Zabel .shift = DMFC_DP_CHAN_6F_29, 8539b9004dSPhilipp Zabel .eot_shift = 23, 8639b9004dSPhilipp Zabel .max_fifo_lines = 1, 8739b9004dSPhilipp Zabel }, 8839b9004dSPhilipp Zabel }; 8939b9004dSPhilipp Zabel 9039b9004dSPhilipp Zabel #define DMFC_NUM_CHANNELS ARRAY_SIZE(dmfcdata) 9139b9004dSPhilipp Zabel 9239b9004dSPhilipp Zabel struct ipu_dmfc_priv; 9339b9004dSPhilipp Zabel 9439b9004dSPhilipp Zabel struct dmfc_channel { 9539b9004dSPhilipp Zabel unsigned slots; 9639b9004dSPhilipp Zabel struct ipu_soc *ipu; 9739b9004dSPhilipp Zabel struct ipu_dmfc_priv *priv; 9839b9004dSPhilipp Zabel const struct dmfc_channel_data *data; 9939b9004dSPhilipp Zabel }; 10039b9004dSPhilipp Zabel 10139b9004dSPhilipp Zabel struct ipu_dmfc_priv { 10239b9004dSPhilipp Zabel struct ipu_soc *ipu; 10339b9004dSPhilipp Zabel struct device *dev; 10439b9004dSPhilipp Zabel struct dmfc_channel channels[DMFC_NUM_CHANNELS]; 10539b9004dSPhilipp Zabel struct mutex mutex; 10639b9004dSPhilipp Zabel void __iomem *base; 10739b9004dSPhilipp Zabel int use_count; 10839b9004dSPhilipp Zabel }; 10939b9004dSPhilipp Zabel 11039b9004dSPhilipp Zabel int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc) 11139b9004dSPhilipp Zabel { 11239b9004dSPhilipp Zabel struct ipu_dmfc_priv *priv = dmfc->priv; 11339b9004dSPhilipp Zabel mutex_lock(&priv->mutex); 11439b9004dSPhilipp Zabel 11539b9004dSPhilipp Zabel if (!priv->use_count) 11639b9004dSPhilipp Zabel ipu_module_enable(priv->ipu, IPU_CONF_DMFC_EN); 11739b9004dSPhilipp Zabel 11839b9004dSPhilipp Zabel priv->use_count++; 11939b9004dSPhilipp Zabel 12039b9004dSPhilipp Zabel mutex_unlock(&priv->mutex); 12139b9004dSPhilipp Zabel 12239b9004dSPhilipp Zabel return 0; 12339b9004dSPhilipp Zabel } 12439b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel); 12539b9004dSPhilipp Zabel 126682b7c1cSLinus Torvalds static void ipu_dmfc_wait_fifos(struct ipu_dmfc_priv *priv) 127682b7c1cSLinus Torvalds { 128682b7c1cSLinus Torvalds unsigned long timeout = jiffies + msecs_to_jiffies(1000); 129682b7c1cSLinus Torvalds 130682b7c1cSLinus Torvalds while ((readl(priv->base + DMFC_STAT) & 0x02fff000) != 0x02fff000) { 131682b7c1cSLinus Torvalds if (time_after(jiffies, timeout)) { 132682b7c1cSLinus Torvalds dev_warn(priv->dev, 133682b7c1cSLinus Torvalds "Timeout waiting for DMFC FIFOs to clear\n"); 134682b7c1cSLinus Torvalds break; 135682b7c1cSLinus Torvalds } 136682b7c1cSLinus Torvalds cpu_relax(); 137682b7c1cSLinus Torvalds } 138682b7c1cSLinus Torvalds } 139682b7c1cSLinus Torvalds 14039b9004dSPhilipp Zabel void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc) 14139b9004dSPhilipp Zabel { 14239b9004dSPhilipp Zabel struct ipu_dmfc_priv *priv = dmfc->priv; 14339b9004dSPhilipp Zabel 14439b9004dSPhilipp Zabel mutex_lock(&priv->mutex); 14539b9004dSPhilipp Zabel 14639b9004dSPhilipp Zabel priv->use_count--; 14739b9004dSPhilipp Zabel 148682b7c1cSLinus Torvalds if (!priv->use_count) { 149682b7c1cSLinus Torvalds ipu_dmfc_wait_fifos(priv); 15039b9004dSPhilipp Zabel ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN); 151682b7c1cSLinus Torvalds } 15239b9004dSPhilipp Zabel 15339b9004dSPhilipp Zabel if (priv->use_count < 0) 15439b9004dSPhilipp Zabel priv->use_count = 0; 15539b9004dSPhilipp Zabel 15639b9004dSPhilipp Zabel mutex_unlock(&priv->mutex); 15739b9004dSPhilipp Zabel } 15839b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel); 15939b9004dSPhilipp Zabel 16027630c20SLiu Ying void ipu_dmfc_config_wait4eot(struct dmfc_channel *dmfc, int width) 16139b9004dSPhilipp Zabel { 16239b9004dSPhilipp Zabel struct ipu_dmfc_priv *priv = dmfc->priv; 16339b9004dSPhilipp Zabel u32 dmfc_gen1; 16439b9004dSPhilipp Zabel 16532c26a56SLiu Ying mutex_lock(&priv->mutex); 16632c26a56SLiu Ying 16739b9004dSPhilipp Zabel dmfc_gen1 = readl(priv->base + DMFC_GENERAL1); 16839b9004dSPhilipp Zabel 16939b9004dSPhilipp Zabel if ((dmfc->slots * 64 * 4) / width > dmfc->data->max_fifo_lines) 17039b9004dSPhilipp Zabel dmfc_gen1 |= 1 << dmfc->data->eot_shift; 17139b9004dSPhilipp Zabel else 17239b9004dSPhilipp Zabel dmfc_gen1 &= ~(1 << dmfc->data->eot_shift); 17339b9004dSPhilipp Zabel 17439b9004dSPhilipp Zabel writel(dmfc_gen1, priv->base + DMFC_GENERAL1); 17539b9004dSPhilipp Zabel 17632c26a56SLiu Ying mutex_unlock(&priv->mutex); 17739b9004dSPhilipp Zabel } 17827630c20SLiu Ying EXPORT_SYMBOL_GPL(ipu_dmfc_config_wait4eot); 17939b9004dSPhilipp Zabel 18039b9004dSPhilipp Zabel struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipu_channel) 18139b9004dSPhilipp Zabel { 18239b9004dSPhilipp Zabel struct ipu_dmfc_priv *priv = ipu->dmfc_priv; 18339b9004dSPhilipp Zabel int i; 18439b9004dSPhilipp Zabel 18539b9004dSPhilipp Zabel for (i = 0; i < DMFC_NUM_CHANNELS; i++) 18639b9004dSPhilipp Zabel if (dmfcdata[i].ipu_channel == ipu_channel) 18739b9004dSPhilipp Zabel return &priv->channels[i]; 18839b9004dSPhilipp Zabel return ERR_PTR(-ENODEV); 18939b9004dSPhilipp Zabel } 19039b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_dmfc_get); 19139b9004dSPhilipp Zabel 19239b9004dSPhilipp Zabel void ipu_dmfc_put(struct dmfc_channel *dmfc) 19339b9004dSPhilipp Zabel { 19439b9004dSPhilipp Zabel } 19539b9004dSPhilipp Zabel EXPORT_SYMBOL_GPL(ipu_dmfc_put); 19639b9004dSPhilipp Zabel 19739b9004dSPhilipp Zabel int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, 19839b9004dSPhilipp Zabel struct clk *ipu_clk) 19939b9004dSPhilipp Zabel { 20039b9004dSPhilipp Zabel struct ipu_dmfc_priv *priv; 20139b9004dSPhilipp Zabel int i; 20239b9004dSPhilipp Zabel 20339b9004dSPhilipp Zabel priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 20439b9004dSPhilipp Zabel if (!priv) 20539b9004dSPhilipp Zabel return -ENOMEM; 20639b9004dSPhilipp Zabel 20739b9004dSPhilipp Zabel priv->base = devm_ioremap(dev, base, PAGE_SIZE); 20839b9004dSPhilipp Zabel if (!priv->base) 20939b9004dSPhilipp Zabel return -ENOMEM; 21039b9004dSPhilipp Zabel 21139b9004dSPhilipp Zabel priv->dev = dev; 21239b9004dSPhilipp Zabel priv->ipu = ipu; 21339b9004dSPhilipp Zabel mutex_init(&priv->mutex); 21439b9004dSPhilipp Zabel 21539b9004dSPhilipp Zabel ipu->dmfc_priv = priv; 21639b9004dSPhilipp Zabel 21739b9004dSPhilipp Zabel for (i = 0; i < DMFC_NUM_CHANNELS; i++) { 21839b9004dSPhilipp Zabel priv->channels[i].priv = priv; 21939b9004dSPhilipp Zabel priv->channels[i].ipu = ipu; 22039b9004dSPhilipp Zabel priv->channels[i].data = &dmfcdata[i]; 221*d7868cb7SLiu Ying 222*d7868cb7SLiu Ying if (dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_BG_SYNC || 223*d7868cb7SLiu Ying dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_FG_SYNC || 224*d7868cb7SLiu Ying dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_DC_SYNC) 225*d7868cb7SLiu Ying priv->channels[i].slots = 2; 22639b9004dSPhilipp Zabel } 22739b9004dSPhilipp Zabel 228*d7868cb7SLiu Ying writel(0x00000050, priv->base + DMFC_WR_CHAN); 229*d7868cb7SLiu Ying writel(0x00005654, priv->base + DMFC_DP_CHAN); 23039b9004dSPhilipp Zabel writel(0x202020f6, priv->base + DMFC_WR_CHAN_DEF); 23139b9004dSPhilipp Zabel writel(0x2020f6f6, priv->base + DMFC_DP_CHAN_DEF); 23239b9004dSPhilipp Zabel writel(0x00000003, priv->base + DMFC_GENERAL1); 23339b9004dSPhilipp Zabel 23439b9004dSPhilipp Zabel return 0; 23539b9004dSPhilipp Zabel } 23639b9004dSPhilipp Zabel 23739b9004dSPhilipp Zabel void ipu_dmfc_exit(struct ipu_soc *ipu) 23839b9004dSPhilipp Zabel { 23939b9004dSPhilipp Zabel } 240