/* * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. * 730 has only 2 McBSP, and both of them are MPU peripherals. */ int omap_mcbsp_init(struct platform_device *pdev) { struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); struct resource *res; int ret = 0; spin_lock_init(&mcbsp->lock); mcbsp->free = true; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); if (!res) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(mcbsp->io_base)) return PTR_ERR(mcbsp->io_base); mcbsp->phys_base = res->start; mcbsp->reg_cache_size = resource_size(res); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); if (!res) mcbsp->phys_dma_base = mcbsp->phys_base; else mcbsp->phys_dma_base = res->start; /* * OMAP1, 2 uses two interrupt lines: TX, RX * OMAP2430, OMAP3 SoC have combined IRQ line as well. * OMAP4 and newer SoC only have the combined IRQ line. * Use the combined IRQ if available since it gives better debugging * possibilities. */ mcbsp->irq = platform_get_irq_byname(pdev, "common"); if (mcbsp->irq == -ENXIO) { mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx"); if (mcbsp->tx_irq == -ENXIO) { mcbsp->irq = platform_get_irq(pdev, 0); mcbsp->tx_irq = 0; } else { mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx"); mcbsp->irq = 0; } } if (!pdev->dev.of_node) { res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); if (!res) { dev_err(&pdev->dev, "invalid tx DMA channel\n"); return -ENODEV; } mcbsp->dma_req[0] = res->start; mcbsp->dma_data[0].filter_data = &mcbsp->dma_req[0]; res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); if (!res) { dev_err(&pdev->dev, "invalid rx DMA channel\n"); return -ENODEV; } mcbsp->dma_req[1] = res->start; mcbsp->dma_data[1].filter_data = &mcbsp->dma_req[1]; } else { mcbsp->dma_data[0].filter_data = "tx"; mcbsp->dma_data[1].filter_data = "rx"; } mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, 0); mcbsp->dma_data[0].maxburst = 4; mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, 1); mcbsp->dma_data[1].maxburst = 4; mcbsp->fclk = clk_get(&pdev->dev, "fck"); if (IS_ERR(mcbsp->fclk)) { ret = PTR_ERR(mcbsp->fclk); dev_err(mcbsp->dev, "unable to get fck: %d\n", ret); return ret; } mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT; if (mcbsp->pdata->buffer_size) { /* * Initially configure the maximum thresholds to a safe value. * The McBSP FIFO usage with these values should not go under * 16 locations. * If the whole FIFO without safety buffer is used, than there * is a possibility that the DMA will be not able to push the * new data on time, causing channel shifts in runtime. */ mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10; mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10; ret = sysfs_create_group(&mcbsp->dev->kobj, &additional_attr_group); if (ret) { dev_err(mcbsp->dev, "Unable to create additional controls\n"); goto err_thres; } } else { mcbsp->max_tx_thres = -EINVAL; mcbsp->max_rx_thres = -EINVAL; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone"); if (res) { ret = omap_st_add(mcbsp, res); if (ret) { dev_err(mcbsp->dev, "Unable to create sidetone controls\n"); goto err_st; } } return 0; err_st: if (mcbsp->pdata->buffer_size) sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); err_thres: clk_put(mcbsp->fclk); return ret; }
static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai); struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; struct omap_pcm_dma_data *dma_data; int dma, bus_id = mcbsp_data->bus_id; int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT; int pkt_size = 0; unsigned long port; unsigned int format, div, framesize, master; dma_data = &omap_mcbsp_dai_dma_params[cpu_dai->id][substream->stream]; dma = omap_mcbsp_dma_ch_params(bus_id, substream->stream); port = omap_mcbsp_dma_reg_params(bus_id, substream->stream); switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: dma_data->data_type = OMAP_DMA_DATA_TYPE_S16; wlen = 16; break; case SNDRV_PCM_FORMAT_S32_LE: dma_data->data_type = OMAP_DMA_DATA_TYPE_S32; wlen = 32; break; default: return -EINVAL; } if (cpu_is_omap34xx() || cpu_is_omap44xx()) { dma_data->set_threshold = omap_mcbsp_set_threshold; /* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ if (omap_mcbsp_get_dma_op_mode(bus_id) == MCBSP_DMA_MODE_THRESHOLD) { int period_words, max_thrsh; period_words = params_period_bytes(params) / (wlen / 8); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) max_thrsh = omap_mcbsp_get_max_tx_threshold( mcbsp_data->bus_id); else max_thrsh = omap_mcbsp_get_max_rx_threshold( mcbsp_data->bus_id); /* * If the period contains less or equal number of words, * we are using the original threshold mode setup: * McBSP threshold = sDMA frame size = period_size * Otherwise we switch to sDMA packet mode: * McBSP threshold = sDMA packet size * sDMA frame size = period size */ if (period_words > max_thrsh) { int divider = 0; /* * Look for the biggest threshold value, which * divides the period size evenly. */ divider = period_words / max_thrsh; if (period_words % max_thrsh) divider++; while (period_words % divider && divider < period_words) divider++; if (divider == period_words) return -EINVAL; pkt_size = period_words / divider; sync_mode = OMAP_DMA_SYNC_PACKET; } else { sync_mode = OMAP_DMA_SYNC_FRAME; } } } dma_data->name = substream->stream ? "Audio Capture" : "Audio Playback"; dma_data->dma_req = dma; dma_data->port_addr = port; dma_data->sync_mode = sync_mode; dma_data->packet_size = pkt_size; snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); if (mcbsp_data->configured) { /* McBSP already configured by another stream */ return 0; } regs->rcr2 &= ~(RPHASE | RFRLEN2(0x7f) | RWDLEN2(7)); regs->xcr2 &= ~(RPHASE | XFRLEN2(0x7f) | XWDLEN2(7)); regs->rcr1 &= ~(RFRLEN1(0x7f) | RWDLEN1(7)); regs->xcr1 &= ~(XFRLEN1(0x7f) | XWDLEN1(7)); format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK; wpf = channels = params_channels(params); if (channels == 2 && (format == SND_SOC_DAIFMT_I2S || format == SND_SOC_DAIFMT_LEFT_J)) { /* Use dual-phase frames */ regs->rcr2 |= RPHASE; regs->xcr2 |= XPHASE; /* Set 1 word per (McBSP) frame for phase1 and phase2 */ wpf--; regs->rcr2 |= RFRLEN2(wpf - 1); regs->xcr2 |= XFRLEN2(wpf - 1); } regs->rcr1 |= RFRLEN1(wpf - 1); regs->xcr1 |= XFRLEN1(wpf - 1); switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: /* Set word lengths */ regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16); regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16); regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16); regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_16); break; case SNDRV_PCM_FORMAT_S32_LE: /* Set word lengths */ regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32); regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32); regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32); regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_32); break; default: /* Unsupported PCM format */ return -EINVAL; } /* In McBSP master modes, FRAME (i.e. sample rate) is generated * by _counting_ BCLKs. Calculate frame size in BCLKs */ master = mcbsp_data->fmt & SND_SOC_DAIFMT_MASTER_MASK; if (master == SND_SOC_DAIFMT_CBS_CFS) { div = mcbsp_data->clk_div ? mcbsp_data->clk_div : 1; framesize = (mcbsp_data->in_freq / div) / params_rate(params); if (framesize < wlen * channels) { printk(KERN_ERR "%s: not enough bandwidth for desired rate and " "channels\n", __func__); return -EINVAL; } } else framesize = wlen * channels; /* Set FS period and length in terms of bit clock periods */ regs->srgr2 &= ~FPER(0xfff); regs->srgr1 &= ~FWID(0xff); switch (format) { case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_LEFT_J: regs->srgr2 |= FPER(framesize - 1); regs->srgr1 |= FWID((framesize >> 1) - 1); break; case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_B: regs->srgr2 |= FPER(framesize - 1); regs->srgr1 |= FWID(0); break; } omap_mcbsp_config(bus_id, &mcbsp_data->regs); mcbsp_data->wlen = wlen; mcbsp_data->configured = 1; return 0; }
int __devinit omap_mcbsp_init(struct platform_device *pdev) { struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); struct resource *res; int ret = 0; spin_lock_init(&mcbsp->lock); mcbsp->free = true; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); if (!res) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(mcbsp->dev, "invalid memory resource\n"); return -ENOMEM; } } if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), dev_name(&pdev->dev))) { dev_err(mcbsp->dev, "memory region already claimed\n"); return -ENODEV; } mcbsp->phys_base = res->start; mcbsp->reg_cache_size = resource_size(res); mcbsp->io_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!mcbsp->io_base) return -ENOMEM; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); if (!res) mcbsp->phys_dma_base = mcbsp->phys_base; else mcbsp->phys_dma_base = res->start; mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx"); mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx"); if (mcbsp->tx_irq == -ENXIO) { mcbsp->tx_irq = platform_get_irq(pdev, 0); mcbsp->rx_irq = 0; } res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); if (!res) { dev_err(&pdev->dev, "invalid rx DMA channel\n"); return -ENODEV; } mcbsp->dma_data[1].name = "Audio Capture"; mcbsp->dma_data[1].dma_req = res->start; mcbsp->dma_data[1].port_addr = omap_mcbsp_dma_reg_params(mcbsp, 1); res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); if (!res) { dev_err(&pdev->dev, "invalid tx DMA channel\n"); return -ENODEV; } mcbsp->dma_data[0].name = "Audio Playback"; mcbsp->dma_data[0].dma_req = res->start; mcbsp->dma_data[0].port_addr = omap_mcbsp_dma_reg_params(mcbsp, 0); mcbsp->fclk = clk_get(&pdev->dev, "fck"); if (IS_ERR(mcbsp->fclk)) { ret = PTR_ERR(mcbsp->fclk); dev_err(mcbsp->dev, "unable to get fck: %d\n", ret); return ret; } mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT; if (mcbsp->pdata->buffer_size) { mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10; mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10; ret = sysfs_create_group(&mcbsp->dev->kobj, &additional_attr_group); if (ret) { dev_err(mcbsp->dev, "Unable to create additional controls\n"); goto err_thres; } } else { mcbsp->max_tx_thres = -EINVAL; mcbsp->max_rx_thres = -EINVAL; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone"); if (res) { ret = omap_st_add(mcbsp, res); if (ret) { dev_err(mcbsp->dev, "Unable to create sidetone controls\n"); goto err_st; } } return 0; err_st: if (mcbsp->pdata->buffer_size) sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); err_thres: clk_put(mcbsp->fclk); return ret; }