From: Iliyan Malchev Date: Wed, 20 Oct 2010 00:50:32 +0000 (-0700) Subject: [ARM] tegra_i2s_audio: add support for selecting tx/rx channels separately X-Git-Tag: firefly_0821_release~9833^2~147^2~11 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=ffd385c7f81f258057b8a63d0864465d6f2b73f3;p=firefly-linux-kernel-4.4.55.git [ARM] tegra_i2s_audio: add support for selecting tx/rx channels separately Signed-off-by: Iliyan Malchev --- diff --git a/arch/arm/mach-tegra/include/mach/audio.h b/arch/arm/mach-tegra/include/mach/audio.h index 6a6b5d08833d..1d631dbddc06 100644 --- a/arch/arm/mach-tegra/include/mach/audio.h +++ b/arch/arm/mach-tegra/include/mach/audio.h @@ -31,6 +31,9 @@ #define I2S_FIFO_TX FIFO1 #define I2S_FIFO_RX FIFO2 +#define TEGRA_AUDIO_ENABLE_TX 1 +#define TEGRA_AUDIO_ENABLE_RX 2 + struct tegra_audio_platform_data { bool master; bool dma_on; @@ -44,6 +47,8 @@ struct tegra_audio_platform_data { int i2s_bus_width; /* 32-bit for 16-bit packed I2S */ int dsp_bus_width; /* 16-bit for DSP data format */ + int mask; /* enable tx and rx? */ + void *driver_data; }; diff --git a/arch/arm/mach-tegra/tegra_i2s_audio.c b/arch/arm/mach-tegra/tegra_i2s_audio.c index dc6e5597a438..583802dd258d 100644 --- a/arch/arm/mach-tegra/tegra_i2s_audio.c +++ b/arch/arm/mach-tegra/tegra_i2s_audio.c @@ -668,46 +668,58 @@ static int setup_dma(struct audio_driver_state *ads) int rc; pr_info("%s\n", __func__); - /* setup audio playback */ - ads->out.buf_phys = dma_map_single(&ads->pdev->dev, ads->out.buffer, - 1 << PCM_BUFFER_MAX_SIZE_ORDER, DMA_TO_DEVICE); - BUG_ON(!ads->out.buf_phys); - setup_dma_tx_request(&ads->out.dma_req, &ads->out); - ads->out.dma_chan = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); - if (!ads->out.dma_chan) { - pr_err("%s: could not allocate output I2S DMA channel: %ld\n", - __func__, PTR_ERR(ads->out.dma_chan)); - rc = -ENODEV; - goto fail_tx; - } - - /* setup audio recording */ - ads->in.buf_phys = dma_map_single(&ads->pdev->dev, ads->in.buffer, - 1 << PCM_BUFFER_MAX_SIZE_ORDER, - DMA_FROM_DEVICE); - BUG_ON(!ads->in.buf_phys); - setup_dma_rx_request(&ads->in.dma_req, &ads->in); - ads->in.dma_chan = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); - if (!ads->in.dma_chan) { - pr_err("%s: could not allocate input I2S DMA channel: %ld\n", - __func__, PTR_ERR(ads->in.dma_chan)); - rc = -ENODEV; - goto fail_rx; + if ((ads->pdata->mask & TEGRA_AUDIO_ENABLE_TX)) { + /* setup audio playback */ + ads->out.buf_phys = dma_map_single(&ads->pdev->dev, + ads->out.buffer, + 1 << PCM_BUFFER_MAX_SIZE_ORDER, + DMA_TO_DEVICE); + BUG_ON(!ads->out.buf_phys); + setup_dma_tx_request(&ads->out.dma_req, &ads->out); + ads->out.dma_chan = + tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); + if (!ads->out.dma_chan) { + pr_err("%s: error allocating output DMA channel: %ld\n", + __func__, PTR_ERR(ads->out.dma_chan)); + rc = -ENODEV; + goto fail_tx; + } + } + + if ((ads->pdata->mask & TEGRA_AUDIO_ENABLE_RX)) { + /* setup audio recording */ + ads->in.buf_phys = dma_map_single(&ads->pdev->dev, + ads->in.buffer, + 1 << PCM_BUFFER_MAX_SIZE_ORDER, + DMA_FROM_DEVICE); + BUG_ON(!ads->in.buf_phys); + setup_dma_rx_request(&ads->in.dma_req, &ads->in); + ads->in.dma_chan = + tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); + if (!ads->in.dma_chan) { + pr_err("%s: error allocating input DMA channel: %ld\n", + __func__, PTR_ERR(ads->in.dma_chan)); + rc = -ENODEV; + goto fail_rx; + } } return 0; fail_rx: - dma_unmap_single(&ads->pdev->dev, ads->in.buf_phys, + if ((ads->pdata->mask & TEGRA_AUDIO_ENABLE_RX)) { + dma_unmap_single(&ads->pdev->dev, ads->in.buf_phys, 1 << PCM_BUFFER_MAX_SIZE_ORDER, DMA_FROM_DEVICE); - tegra_dma_free_channel(ads->in.dma_chan); - ads->in.dma_chan = 0; - + tegra_dma_free_channel(ads->in.dma_chan); + ads->in.dma_chan = 0; + } fail_tx: - dma_unmap_single(&ads->pdev->dev, ads->out.buf_phys, + if ((ads->pdata->mask & TEGRA_AUDIO_ENABLE_TX)) { + dma_unmap_single(&ads->pdev->dev, ads->out.buf_phys, 1 << PCM_BUFFER_MAX_SIZE_ORDER, DMA_TO_DEVICE); - tegra_dma_free_channel(ads->out.dma_chan); - ads->out.dma_chan = 0; + tegra_dma_free_channel(ads->out.dma_chan); + ads->out.dma_chan = 0; + } return rc; } @@ -2204,6 +2216,12 @@ static int tegra_audio_probe(struct platform_device *pdev) state->pdata->driver_data = state; BUG_ON(!state->pdata); + if (!(state->pdata->mask & + (TEGRA_AUDIO_ENABLE_TX | TEGRA_AUDIO_ENABLE_RX))) { + dev_err(&pdev->dev, "neither tx nor rx is enabled!\n"); + return -EIO; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "no mem resource!\n"); @@ -2223,9 +2241,6 @@ static int tegra_audio_probe(struct platform_device *pdev) return -EIO; } - state->out.i2s_fifo_atn_level = I2S_FIFO_ATN_LVL_FOUR_SLOTS; - state->in.i2s_fifo_atn_level = I2S_FIFO_ATN_LVL_FOUR_SLOTS; - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!res) { dev_err(&pdev->dev, "no dma resource!\n"); @@ -2273,98 +2288,101 @@ static int tegra_audio_probe(struct platform_device *pdev) } clk_enable(audio_sync_clk); + i2s_enable_fifos(state->i2s_base, 0); /* disable interrupts from I2S */ i2s_fifo_clear(state->i2s_base, I2S_FIFO_TX); i2s_fifo_clear(state->i2s_base, I2S_FIFO_RX); - i2s_enable_fifos(state->i2s_base, 0); - i2s_set_left_right_control_polarity(state->i2s_base, 0); /* default */ - if (state->pdata->master) i2s_set_channel_bit_count(state->i2s_base, 44100, clk_get_rate(i2s_clk)); i2s_set_master(state->i2s_base, state->pdata->master); - i2s_set_fifo_mode(state->i2s_base, I2S_FIFO_TX, 1); i2s_set_fifo_mode(state->i2s_base, I2S_FIFO_RX, 0); - i2s_set_bit_format(state->i2s_base, state->pdata->mode); i2s_set_bit_size(state->i2s_base, state->pdata->bit_size); i2s_set_fifo_format(state->i2s_base, state->pdata->fifo_fmt); - state->out.opened = 0; - state->out.active = false; - mutex_init(&state->out.lock); - init_completion(&state->out.fifo_completion); - init_completion(&state->out.stop_completion); - spin_lock_init(&state->out.dma_req_lock); - state->out.buf_phys = 0; - state->out.dma_chan = NULL; - state->out.dma_has_it = false; - - state->in.opened = 0; - state->in.active = false; - mutex_init(&state->in.lock); - init_completion(&state->in.fifo_completion); - init_completion(&state->in.stop_completion); - spin_lock_init(&state->in.dma_req_lock); - state->in.buf_phys = 0; - state->in.dma_chan = NULL; - state->in.dma_has_it = false; - - state->out.buffer = 0; - state->out.buf_config.size = PCM_BUFFER_MAX_SIZE_ORDER; - state->out.buf_config.threshold = PCM_BUFFER_THRESHOLD_ORDER; - state->out.buf_config.chunk = PCM_BUFFER_DMA_CHUNK_SIZE_ORDER; - rc = init_stream_buffer(&state->out, &state->out.buf_config, 0); - if (rc < 0) - return rc; - - state->in.buffer = 0; - state->in.buf_config.size = PCM_BUFFER_MAX_SIZE_ORDER; - state->in.buf_config.threshold = PCM_BUFFER_THRESHOLD_ORDER; - state->in.buf_config.chunk = PCM_BUFFER_DMA_CHUNK_SIZE_ORDER; - rc = init_stream_buffer(&state->in, &state->in.buf_config, - PCM_IN_BUFFER_PADDING); - if (rc < 0) - return rc; + if ((state->pdata->mask & TEGRA_AUDIO_ENABLE_TX)) { + state->out.opened = 0; + state->out.active = false; + mutex_init(&state->out.lock); + init_completion(&state->out.fifo_completion); + init_completion(&state->out.stop_completion); + spin_lock_init(&state->out.dma_req_lock); + state->out.buf_phys = 0; + state->out.dma_chan = NULL; + state->out.dma_has_it = false; + + state->out.i2s_fifo_atn_level = I2S_FIFO_ATN_LVL_FOUR_SLOTS; + state->out.buffer = 0; + state->out.buf_config.size = PCM_BUFFER_MAX_SIZE_ORDER; + state->out.buf_config.threshold = PCM_BUFFER_THRESHOLD_ORDER; + state->out.buf_config.chunk = PCM_BUFFER_DMA_CHUNK_SIZE_ORDER; + rc = init_stream_buffer(&state->out, &state->out.buf_config, 0); + if (rc < 0) + return rc; - pm_qos_add_request(&state->in.pm_qos, PM_QOS_CPU_DMA_LATENCY, - PM_QOS_DEFAULT_VALUE); - pm_qos_add_request(&state->out.pm_qos, PM_QOS_CPU_DMA_LATENCY, + pm_qos_add_request(&state->out.pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); - if (request_irq(state->irq, i2s_interrupt, - IRQF_DISABLED, state->pdev->name, state) < 0) { - dev_err(&pdev->dev, - "%s: could not register handler for irq %d\n", - __func__, state->irq); - return -EIO; - } - - rc = setup_misc_device(&state->misc_out, + rc = setup_misc_device(&state->misc_out, &tegra_audio_out_fops, "audio%d_out", state->pdev->id); - if (rc < 0) - return rc; + if (rc < 0) + return rc; - rc = setup_misc_device(&state->misc_out_ctl, - &tegra_audio_out_ctl_fops, - "audio%d_out_ctl", state->pdev->id); - if (rc < 0) - return rc; + rc = setup_misc_device(&state->misc_out_ctl, + &tegra_audio_out_ctl_fops, + "audio%d_out_ctl", state->pdev->id); + if (rc < 0) + return rc; + } + + if ((state->pdata->mask & TEGRA_AUDIO_ENABLE_RX)) { + state->in.opened = 0; + state->in.active = false; + mutex_init(&state->in.lock); + init_completion(&state->in.fifo_completion); + init_completion(&state->in.stop_completion); + spin_lock_init(&state->in.dma_req_lock); + state->in.buf_phys = 0; + state->in.dma_chan = NULL; + state->in.dma_has_it = false; + + state->in.i2s_fifo_atn_level = I2S_FIFO_ATN_LVL_FOUR_SLOTS; + state->in.buffer = 0; + state->in.buf_config.size = PCM_BUFFER_MAX_SIZE_ORDER; + state->in.buf_config.threshold = PCM_BUFFER_THRESHOLD_ORDER; + state->in.buf_config.chunk = PCM_BUFFER_DMA_CHUNK_SIZE_ORDER; + rc = init_stream_buffer(&state->in, &state->in.buf_config, + PCM_IN_BUFFER_PADDING); + if (rc < 0) + return rc; + + pm_qos_add_request(&state->in.pm_qos, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); - rc = setup_misc_device(&state->misc_in, + rc = setup_misc_device(&state->misc_in, &tegra_audio_in_fops, "audio%d_in", state->pdev->id); - if (rc < 0) - return rc; + if (rc < 0) + return rc; - rc = setup_misc_device(&state->misc_in_ctl, + rc = setup_misc_device(&state->misc_in_ctl, &tegra_audio_in_ctl_fops, "audio%d_in_ctl", state->pdev->id); - if (rc < 0) - return rc; + if (rc < 0) + return rc; + } + + if (request_irq(state->irq, i2s_interrupt, + IRQF_DISABLED, state->pdev->name, state) < 0) { + dev_err(&pdev->dev, + "%s: could not register handler for irq %d\n", + __func__, state->irq); + return -EIO; + } rc = setup_misc_device(&state->misc_ctl, &tegra_audio_ctl_fops,