diff options
Diffstat (limited to 'sound/soc/imx/imx-hdmi-dma.c')
-rw-r--r-- | sound/soc/imx/imx-hdmi-dma.c | 255 |
1 files changed, 243 insertions, 12 deletions
diff --git a/sound/soc/imx/imx-hdmi-dma.c b/sound/soc/imx/imx-hdmi-dma.c index 2f4799d9414c..a743d10ba8a8 100644 --- a/sound/soc/imx/imx-hdmi-dma.c +++ b/sound/soc/imx/imx-hdmi-dma.c @@ -36,12 +36,35 @@ #include <mach/mxc_hdmi.h> #include "imx-hdmi.h" +#include <linux/dmaengine.h> +#include <mach/dma.h> +#include <linux/iram_alloc.h> +#include <linux/io.h> +#include <asm/mach/map.h> +#include <mach/hardware.h> #define HDMI_DMA_BURST_UNSPECIFIED_LEGNTH 0 #define HDMI_DMA_BURST_INCR4 1 #define HDMI_DMA_BURST_INCR8 2 #define HDMI_DMA_BURST_INCR16 3 + + +#define HDMI_BASE_ADDR 0x00120000 + +struct hdmi_sdma_script_data { + int control_reg_addr; + int status_reg_addr; + int dma_start_addr; + u32 buffer[20]; +}; + +struct imx_hdmi_sdma_params { + u32 buffer_num; + dma_addr_t phyaddr; +}; + + struct imx_hdmi_dma_runtime_data { struct snd_pcm_substream *tx_substream; @@ -71,6 +94,15 @@ struct imx_hdmi_dma_runtime_data { bool tx_active; spinlock_t irq_lock; + + /*SDMA part*/ + unsigned int dma_event; + struct dma_chan *dma_channel; + struct imx_dma_data dma_data; + struct hdmi_sdma_script_data *hdmi_sdma_t; + dma_addr_t phy_hdmi_sdma_t; + struct dma_async_tx_descriptor *desc; + struct imx_hdmi_sdma_params sdma_params; }; /* bit 0:0:0:b:p(0):c:(u)0:(v)0*/ @@ -509,7 +541,7 @@ static void hdmi_dma_copy_24(u32 *src, u32 *dest, int framecount, int channelcou #endif static void hdmi_dma_mmap_copy(struct snd_pcm_substream *substream, - int offset, int count) + int offset, int count) { struct snd_pcm_runtime *runtime = substream->runtime; struct imx_hdmi_dma_runtime_data *rtd = runtime->private_data; @@ -545,6 +577,50 @@ static void hdmi_dma_mmap_copy(struct snd_pcm_substream *substream, } } +static void hdmi_sdma_isr(void *data) +{ + struct imx_hdmi_dma_runtime_data *rtd = data; + struct snd_pcm_substream *substream = rtd->tx_substream; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned long offset, count, space_to_end, appl_bytes; + unsigned long flags; + + spin_lock_irqsave(&rtd->irq_lock, flags); + + if (runtime && runtime->dma_area && rtd->tx_active) { + + rtd->offset += rtd->period_bytes; + rtd->offset %= rtd->period_bytes * rtd->periods; + + /* For mmap access, need to copy data from dma_buffer + * to hw_buffer and add the frame info. */ + if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { + appl_bytes = frames_to_bytes(runtime, + runtime->control->appl_ptr); + offset = rtd->appl_bytes % rtd->buffer_bytes; + space_to_end = rtd->buffer_bytes - offset; + count = appl_bytes - rtd->appl_bytes; + rtd->appl_bytes = appl_bytes; + + if (count <= space_to_end) { + hdmi_dma_mmap_copy(substream, offset, count); + } else { + hdmi_dma_mmap_copy(substream, + offset, space_to_end); + hdmi_dma_mmap_copy(substream, + 0, count - space_to_end); + } + } + snd_pcm_period_elapsed(substream); + + } + + spin_unlock_irqrestore(&rtd->irq_lock, flags); + + return; +} + + static irqreturn_t hdmi_dma_isr(int irq, void *dev_id) { struct imx_hdmi_dma_runtime_data *rtd = dev_id; @@ -815,11 +891,122 @@ static int hdmi_dma_copy(struct snd_pcm_substream *substream, int channel, return 0; } +static bool hdmi_filter(struct dma_chan *chan, void *param) +{ + + if (!imx_dma_is_general_purpose(chan)) + return false; + + chan->private = param; + return true; +} + + +static int hdmi_init_sdma_buffer(struct imx_hdmi_dma_runtime_data *params) +{ + int i; + u32 *tmp_addr1, *tmp_addr2; + + if (!params->hdmi_sdma_t) { + dev_err(¶ms->dma_channel->dev->device, + "hdmi private addr invalid!!!\n"); + return -EINVAL; + } + + params->hdmi_sdma_t->control_reg_addr = + HDMI_BASE_ADDR + HDMI_AHB_DMA_START; + params->hdmi_sdma_t->status_reg_addr = + HDMI_BASE_ADDR + HDMI_IH_AHBDMAAUD_STAT0; + params->hdmi_sdma_t->dma_start_addr = + HDMI_BASE_ADDR + HDMI_AHB_DMA_STRADDR0; + + tmp_addr1 = (u32 *)params->hdmi_sdma_t + 3; + tmp_addr2 = (u32 *)params->hdmi_sdma_t + 4; + for (i = 0; i < params->sdma_params.buffer_num; i++) { + *tmp_addr1 = params->hw_buffer.addr + + i * params->period_bytes * params->buffer_ratio; + *tmp_addr2 = *tmp_addr1 + params->dma_period_bytes - 1; + tmp_addr1 += 2; + tmp_addr2 += 2; + } + + return 0; +} + +static int hdmi_sdma_alloc(struct imx_hdmi_dma_runtime_data *params) +{ + dma_cap_mask_t mask; + + params->dma_data.peripheral_type = IMX_DMATYPE_HDMI; + params->dma_data.priority = DMA_PRIO_MEDIUM; + params->dma_data.dma_request = MX6Q_DMA_REQ_EXT_DMA_REQ_0; + params->dma_data.private = ¶ms->sdma_params; + + /* Try to grab a DMA channel */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + params->dma_channel = dma_request_channel( + mask, hdmi_filter, ¶ms->dma_data); + if (params->dma_channel == NULL) { + dev_err(¶ms->dma_channel->dev->device, + "HDMI:unable to alloc dma_channel channel\n"); + return -EBUSY; + } + return 0; +} + +static int hdmi_sdma_config(struct imx_hdmi_dma_runtime_data *params) +{ + struct dma_slave_config slave_config; + int ret; + + slave_config.direction = DMA_TRANS_NONE; + ret = dmaengine_slave_config(params->dma_channel, &slave_config); + if (ret) { + dev_err(¶ms->dma_channel->dev->device, + "%s failed\r\n", __func__); + return -EINVAL; + } + + params->desc = + params->dma_channel->device->device_prep_dma_cyclic( + params->dma_channel, 0, + 0, + 0, + DMA_TRANS_NONE); + if (!params->desc) { + dev_err(¶ms->dma_channel->dev->device, + "cannot prepare slave dma\n"); + return -EINVAL; + } + + params->desc->callback = hdmi_sdma_isr; + params->desc->callback_param = (void *)hdmi_dma_priv; + + return 0; +} + + + +static int hdmi_dma_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct imx_hdmi_dma_runtime_data *params = runtime->private_data; + if ((mx6q_revision() > IMX_CHIP_REVISION_1_1) && + (params->dma_channel)) { + dma_release_channel(params->dma_channel); + params->dma_channel = NULL; + } + return 0; +} + static int hdmi_dma_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; struct imx_hdmi_dma_runtime_data *rtd = runtime->private_data; + int err; rtd->buffer_bytes = params_buffer_bytes(params); rtd->periods = params_periods(params); @@ -850,6 +1037,23 @@ static int hdmi_dma_hw_params(struct snd_pcm_substream *substream, } rtd->dma_period_bytes = rtd->period_bytes * rtd->buffer_ratio; + if ((mx6q_revision() > IMX_CHIP_REVISION_1_1)) { + rtd->sdma_params.buffer_num = rtd->periods; + rtd->sdma_params.phyaddr = rtd->phy_hdmi_sdma_t; + + /* Allocate SDMA channel for HDMI */ + err = hdmi_init_sdma_buffer(rtd); + if (err) + return err; + + err = hdmi_sdma_alloc(rtd); + if (err) + return err; + + err = hdmi_sdma_config(rtd); + if (err) + return err; + } snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); @@ -892,6 +1096,8 @@ static int hdmi_dma_trigger(struct snd_pcm_substream *substream, int cmd) hdmi_dma_start(); hdmi_dma_irq_mask(0); hdmi_set_dma_mode(1); + if ((mx6q_revision() > IMX_CHIP_REVISION_1_1)) + dmaengine_submit(rtd->desc); break; case SNDRV_PCM_TRIGGER_STOP: @@ -901,6 +1107,8 @@ static int hdmi_dma_trigger(struct snd_pcm_substream *substream, int cmd) hdmi_dma_stop(); hdmi_set_dma_mode(0); hdmi_dma_irq_mask(1); + if ((mx6q_revision() > IMX_CHIP_REVISION_1_1)) + dmaengine_terminate_all(rtd->dma_channel); break; default: @@ -947,7 +1155,10 @@ static void hdmi_dma_irq_enable(struct imx_hdmi_dma_runtime_data *rtd) spin_lock_irqsave(&hdmi_dma_priv->irq_lock, flags); hdmi_dma_clear_irq_status(0xff); - hdmi_dma_irq_mute(0); + if ((mx6q_revision() > IMX_CHIP_REVISION_1_1)) + hdmi_dma_irq_mute(1); + else + hdmi_dma_irq_mute(0); hdmi_dma_irq_mask(0); hdmi_mask(0); @@ -1017,6 +1228,7 @@ static struct snd_pcm_ops imx_hdmi_dma_pcm_ops = { .close = hdmi_dma_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = hdmi_dma_hw_params, + .hw_free = hdmi_dma_hw_free, .trigger = hdmi_dma_trigger, .pointer = hdmi_dma_pointer, .copy = hdmi_dma_copy, @@ -1095,7 +1307,7 @@ static void imx_hdmi_dma_pcm_free(struct snd_pcm *pcm) continue; dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); + buf->area, buf->addr); buf->area = NULL; } @@ -1103,7 +1315,7 @@ static void imx_hdmi_dma_pcm_free(struct snd_pcm *pcm) buf = &hdmi_dma_priv->hw_buffer; if (buf->area) { dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); + buf->area, buf->addr); buf->area = NULL; } } @@ -1122,7 +1334,17 @@ static int __devinit imx_soc_platform_probe(struct platform_device *pdev) hdmi_dma_priv = kzalloc(sizeof(*hdmi_dma_priv), GFP_KERNEL); if (hdmi_dma_priv == NULL) return -ENOMEM; - /*To alloc a buffer non cacheable for hdmi script use*/ + + if ((mx6q_revision() > IMX_CHIP_REVISION_1_1)) { + /*To alloc a buffer non cacheable for hdmi script use*/ + hdmi_dma_priv->hdmi_sdma_t = + dma_alloc_coherent(NULL, + sizeof(struct hdmi_sdma_script_data), + &hdmi_dma_priv->phy_hdmi_sdma_t, + GFP_KERNEL); + if (hdmi_dma_priv->hdmi_sdma_t == NULL) + return -ENOMEM; + } hdmi_dma_priv->tx_active = false; spin_lock_init(&hdmi_dma_priv->irq_lock); @@ -1143,14 +1365,17 @@ static int __devinit imx_soc_platform_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Unable to get HDMI ahb clk: %d\n", ret); goto e_clk_get2; } - - if (request_irq(hdmi_dma_priv->irq, hdmi_dma_isr, IRQF_SHARED, - "hdmi dma", hdmi_dma_priv)) { - dev_err(&pdev->dev, "MXC hdmi: failed to request irq %d\n", - hdmi_dma_priv->irq); - ret = -EBUSY; - goto e_irq; + if ((mx6q_revision() <= IMX_CHIP_REVISION_1_1)) { + if (request_irq(hdmi_dma_priv->irq, hdmi_dma_isr, IRQF_SHARED, + "hdmi dma", hdmi_dma_priv)) { + dev_err(&pdev->dev, + "MXC hdmi: failed to request irq %d\n", + hdmi_dma_priv->irq); + ret = -EBUSY; + goto e_irq; + } } + ret = snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2); if (ret) goto e_irq; @@ -1170,6 +1395,12 @@ e_clk_get1: static int __devexit imx_soc_platform_remove(struct platform_device *pdev) { free_irq(hdmi_dma_priv->irq, hdmi_dma_priv); + if ((mx6q_revision() > IMX_CHIP_REVISION_1_1)) { + dma_free_coherent(NULL, + sizeof(struct hdmi_sdma_script_data), + hdmi_dma_priv->hdmi_sdma_t, + hdmi_dma_priv->phy_hdmi_sdma_t); + } snd_soc_unregister_platform(&pdev->dev); kfree(hdmi_dma_priv); return 0; |