/* * arch/arm/mach-tegra/tegra_spdif_audio.c * * S/PDIF audio driver for NVIDIA Tegra SoCs * * Copyright (c) 2008-2011, NVIDIA Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clock.h" #define PCM_BUFFER_MAX_SIZE_ORDER (PAGE_SHIFT) #define SPDIF_MAX_NUM_BUFS 4 /* Todo: Add IOCTL to configure the number of buffers. */ #define SPDIF_DEFAULT_TX_NUM_BUFS 2 #define SPDIF_DEFAULT_RX_NUM_BUFS 2 /* per stream (input/output) */ struct audio_stream { int opened; struct mutex lock; bool active; /* is DMA in progress? */ int num_bufs; void *buffer[SPDIF_MAX_NUM_BUFS]; dma_addr_t buf_phy[SPDIF_MAX_NUM_BUFS]; struct completion comp[SPDIF_MAX_NUM_BUFS]; struct tegra_dma_req dma_req[SPDIF_MAX_NUM_BUFS]; int last_queued; int spdif_fifo_atn_level; struct tegra_dma_channel *dma_chan; bool stop; struct completion stop_completion; spinlock_t dma_req_lock; struct work_struct allow_suspend_work; struct wake_lock wake_lock; char wake_lock_name[100]; }; struct audio_driver_state { struct list_head next; struct platform_device *pdev; struct tegra_audio_platform_data *pdata; phys_addr_t spdif_phys; unsigned long spdif_base; unsigned long dma_req_sel; bool fifo_init; int irq; struct miscdevice misc_out; struct miscdevice misc_out_ctl; struct audio_stream out; }; static inline bool pending_buffer_requests(struct audio_stream *stream) { int i; for (i = 0; i < stream->num_bufs; i++) if (!completion_done(&stream->comp[i])) return true; return false; } static inline int buf_size(struct audio_stream *s __attribute__((unused))) { return 1 << PCM_BUFFER_MAX_SIZE_ORDER; } static inline struct audio_driver_state *ads_from_misc_out(struct file *file) { struct miscdevice *m = file->private_data; struct audio_driver_state *ads = container_of(m, struct audio_driver_state, misc_out); BUG_ON(!ads); return ads; } static inline struct audio_driver_state *ads_from_misc_out_ctl( struct file *file) { struct miscdevice *m = file->private_data; struct audio_driver_state *ads = container_of(m, struct audio_driver_state, misc_out_ctl); BUG_ON(!ads); return ads; } static inline struct audio_driver_state *ads_from_out( struct audio_stream *aos) { return container_of(aos, struct audio_driver_state, out); } static inline void prevent_suspend(struct audio_stream *as) { pr_debug("%s\n", __func__); cancel_work_sync(&as->allow_suspend_work); wake_lock(&as->wake_lock); } static void allow_suspend_worker(struct work_struct *w) { struct audio_stream *as = container_of(w, struct audio_stream, allow_suspend_work); pr_debug("%s\n", __func__); wake_unlock(&as->wake_lock); } static inline void allow_suspend(struct audio_stream *as) { schedule_work(&as->allow_suspend_work); } static int set_spdif_clock(struct audio_driver_state *state, unsigned int sample_rate) { unsigned int clock_freq = 0; struct clk *spdif_clk; struct clk *pll_a_out0_clk = clk_get_sys(NULL, "pll_a_out0"); switch (sample_rate) { case 32000: clock_freq = 4096000; /* 4.0960 MHz */ break; case 44100: clock_freq = 5644800; /* 5.6448 MHz */ break; case 48000: clock_freq = 6144000; /* 6.1440MHz */ break; case 88200: clock_freq = 11289600; /* 11.2896 MHz */ break; case 96000: clock_freq = 12288000; /* 12.288 MHz */ break; case 176400: clock_freq = 22579200; /* 22.5792 MHz */ break; case 192000: clock_freq = 24576000; /* 24.5760 MHz */ break; default: return -1; } spdif_clk = clk_get(&state->pdev->dev, NULL); if (!spdif_clk) { dev_err(&state->pdev->dev, "%s: could not get spdif clock\n", __func__); return -EIO; } /* set spdif parent as plla */ clk_set_parent(spdif_clk, pll_a_out0_clk); clk_set_rate(spdif_clk, clock_freq); if (clk_enable(spdif_clk)) { dev_err(&state->pdev->dev, "%s: failed to enable spdif_clk clock\n", __func__); return -EIO; } pr_info("%s: spdif_clk rate %ld\n", __func__, clk_get_rate(spdif_clk)); /*FIXME: Proper position for this code will be Display driver */ /* can do based on hdmi plugin */ #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) { struct clk *hda2codec_clk =0; hda2codec_clk = clk_get_sys("hda2codec_2x", NULL); if (IS_ERR_OR_NULL(hda2codec_clk)) { dev_err(&state->pdev->dev, "couldn't get hda2codec_2x clock\n"); goto clock_fail; } if (clk_enable(hda2codec_clk)) { dev_err(&state->pdev->dev, "failed to enable hda2codec_2x clock\n"); goto clock_fail; } return 0; clock_fail: if (spdif_clk) clk_disable(spdif_clk); return -EIO; } #else return 0; #endif } static int init_stream_buffer(struct audio_stream *, int); static int setup_dma(struct audio_driver_state *); static void tear_down_dma(struct audio_driver_state *); static void stop_dma_playback(struct audio_stream *); struct sound_ops { int (*setup)(struct audio_driver_state *); void (*tear_down)(struct audio_driver_state *); void (*stop_playback)(struct audio_stream *); }; static const struct sound_ops dma_sound_ops = { .setup = setup_dma, .tear_down = tear_down_dma, .stop_playback = stop_dma_playback, }; static const struct sound_ops *sound_ops = &dma_sound_ops; static bool stop_playback_if_necessary(struct audio_stream *aos) { unsigned long flags; spin_lock_irqsave(&aos->dma_req_lock, flags); pr_debug("%s\n", __func__); if (!pending_buffer_requests(aos)) { pr_debug("%s: no more data to play back\n", __func__); sound_ops->stop_playback(aos); spin_unlock_irqrestore(&aos->dma_req_lock, flags); allow_suspend(aos); return true; } spin_unlock_irqrestore(&aos->dma_req_lock, flags); return false; } /* playback */ static bool wait_till_stopped(struct audio_stream *as) { int rc; pr_debug("%s: wait for completion\n", __func__); rc = wait_for_completion_timeout( &as->stop_completion, HZ); if (!rc) pr_err("%s: wait timed out", __func__); if (rc < 0) pr_err("%s: wait error %d\n", __func__, rc); allow_suspend(as); pr_debug("%s: done: %d\n", __func__, rc); return true; } /* Ask for playback to stop. The _nosync means that * as->lock has to be locked by the caller. */ static void request_stop_nosync(struct audio_stream *as) { int i; pr_debug("%s\n", __func__); if (!as->stop) { as->stop = true; if (pending_buffer_requests(as)) wait_till_stopped(as); for (i = 0; i < as->num_bufs; i++) { init_completion(&as->comp[i]); complete(&as->comp[i]); } } if (!tegra_dma_is_empty(as->dma_chan)) pr_err("%s: DMA not empty!\n", __func__); /* Stop the DMA then dequeue anything that's in progress. */ tegra_dma_cancel(as->dma_chan); as->active = false; /* applies to recording only */ pr_debug("%s: done\n", __func__); } static void setup_dma_tx_request(struct tegra_dma_req *req, struct audio_stream *aos); static int setup_dma(struct audio_driver_state *ads) { int rc, i; pr_info("%s\n", __func__); /* setup audio playback */ for (i = 0; i < ads->out.num_bufs; i++) { ads->out.buf_phy[i] = dma_map_single(&ads->pdev->dev, ads->out.buffer[i], buf_size(&ads->out), DMA_TO_DEVICE); BUG_ON(!ads->out.buf_phy[i]); setup_dma_tx_request(&ads->out.dma_req[i], &ads->out); ads->out.dma_req[i].source_addr = ads->out.buf_phy[i]; } ads->out.dma_chan = tegra_dma_allocate_channel(TEGRA_DMA_MODE_CONTINUOUS_SINGLE, "spdif_tx_req_%d", ads->dma_req_sel); if (!ads->out.dma_chan) { pr_err("%s: error alloc output DMA channel: %ld\n", __func__, PTR_ERR(ads->out.dma_chan)); rc = -ENODEV; goto fail_tx; } return 0; fail_tx: for (i = 0; i < ads->out.num_bufs; i++) { dma_unmap_single(&ads->pdev->dev, ads->out.buf_phy[i], buf_size(&ads->out), DMA_TO_DEVICE); ads->out.buf_phy[i] = 0; } tegra_dma_free_channel(ads->out.dma_chan); ads->out.dma_chan = 0; return rc; } static void tear_down_dma(struct audio_driver_state *ads) { int i; pr_info("%s\n", __func__); tegra_dma_free_channel(ads->out.dma_chan); for (i = 0; i < ads->out.num_bufs; i++) { dma_unmap_single(&ads->pdev->dev, ads->out.buf_phy[i], buf_size(&ads->out), DMA_TO_DEVICE); ads->out.buf_phy[i] = 0; } ads->out.dma_chan = NULL; } static void dma_tx_complete_callback(struct tegra_dma_req *req) { struct audio_stream *aos = req->dev; unsigned req_num; req_num = req - aos->dma_req; pr_debug("%s: completed buffer %d size %d\n", __func__, req_num, req->bytes_transferred); BUG_ON(req_num >= aos->num_bufs); complete(&aos->comp[req_num]); if (!pending_buffer_requests(aos)) { pr_debug("%s: Playback underflow", __func__); complete(&aos->stop_completion); } } static void setup_dma_tx_request(struct tegra_dma_req *req, struct audio_stream *aos) { struct audio_driver_state *ads = ads_from_out(aos); memset(req, 0, sizeof(*req)); req->complete = dma_tx_complete_callback; req->dev = aos; req->to_memory = false; req->dest_addr = spdif_get_fifo_phy_base(ads->spdif_phys, AUDIO_TX_MODE); req->dest_bus_width = 32; req->dest_wrap = 4; req->source_wrap = 0; req->source_bus_width = 32; req->req_sel = ads->dma_req_sel; } static int start_playback(struct audio_stream *aos, struct tegra_dma_req *req) { int rc; unsigned long flags; struct audio_driver_state *ads = ads_from_out(aos); pr_debug("%s: (writing %d)\n", __func__, req->size); spin_lock_irqsave(&aos->dma_req_lock, flags); #if 0 spdif_fifo_clear(ads->spdif_base); #endif spdif_fifo_set_attention_level(ads->spdif_base, AUDIO_TX_MODE, ads->out.spdif_fifo_atn_level); if (ads->fifo_init) { spdif_set_bit_mode(ads->spdif_base, SPDIF_BIT_MODE_MODE16BIT); spdif_set_fifo_packed(ads->spdif_base, 1); ads->fifo_init = false; } spdif_fifo_enable(ads->spdif_base, AUDIO_TX_MODE, 1); rc = tegra_dma_enqueue_req(aos->dma_chan, req); spin_unlock_irqrestore(&aos->dma_req_lock, flags); if (rc) pr_err("%s: could not enqueue TX DMA req\n", __func__); return rc; } /* Called with aos->dma_req_lock taken. */ static void stop_dma_playback(struct audio_stream *aos) { int spin = 0; struct audio_driver_state *ads = ads_from_out(aos); pr_debug("%s\n", __func__); spdif_fifo_enable(ads->spdif_base, AUDIO_TX_MODE, 0); while ((spdif_get_status(ads->spdif_base, AUDIO_TX_MODE) & SPDIF_STATUS_0_TX_BSY) && spin < 100) { udelay(10); if (spin++ > 50) pr_info("%s: spin %d\n", __func__, spin); } if (spin == 100) pr_warn("%s: spinny\n", __func__); ads->fifo_init = true; } #if defined(CONFIG_ARCH_TEGRA_2x_SOC) static irqreturn_t spdif_interrupt(int irq, void *data) { struct audio_driver_state *ads = data; u32 status = spdif_get_status(ads->spdif_base, AUDIO_TX_MODE); pr_debug("%s: %08x\n", __func__, status); /* if (status & SPDIF_STATUS_0_TX_ERR) */ spdif_ack_status(ads->spdif_base); pr_debug("%s: done %08x\n", __func__, spdif_get_status(ads->spdif_base, AUDIO_TX_MODE)); return IRQ_HANDLED; } #endif static ssize_t tegra_spdif_write(struct file *file, const char __user *buf, size_t size, loff_t *off) { ssize_t rc = 0; int out_buf; struct tegra_dma_req *req; struct audio_driver_state *ads = ads_from_misc_out(file); mutex_lock(&ads->out.lock); if (!IS_ALIGNED(size, 4) || size < 4 || size > buf_size(&ads->out)) { pr_err("%s: invalid user size %d\n", __func__, size); rc = -EINVAL; goto done; } pr_debug("%s: write %d bytes\n", __func__, size); if (ads->out.stop) { pr_debug("%s: playback has been cancelled\n", __func__); goto done; } /* Decide which buf is next. */ out_buf = (ads->out.last_queued + 1) % ads->out.num_bufs; req = &ads->out.dma_req[out_buf]; /* Wait for the buffer to be emptied (complete). The maximum timeout * value could be calculated dynamically based on buf_size(&ads->out). * For a buffer size of 16k, at 44.1kHz/stereo/16-bit PCM, you would * have ~93ms. */ pr_debug("%s: waiting for buffer %d\n", __func__, out_buf); rc = wait_for_completion_interruptible_timeout( &ads->out.comp[out_buf], HZ); if (!rc) { pr_err("%s: timeout", __func__); rc = -ETIMEDOUT; goto done; } else if (rc < 0) { pr_err("%s: wait error %d", __func__, rc); goto done; } /* Fill the buffer and enqueue it. */ pr_debug("%s: acquired buffer %d, copying data\n", __func__, out_buf); rc = copy_from_user(ads->out.buffer[out_buf], buf, size); if (rc) { rc = -EFAULT; goto done; } prevent_suspend(&ads->out); req->size = size; dma_sync_single_for_device(NULL, req->source_addr, req->size, DMA_TO_DEVICE); ads->out.last_queued = out_buf; init_completion(&ads->out.stop_completion); rc = start_playback(&ads->out, req); if (!rc) rc = size; else allow_suspend(&ads->out); done: mutex_unlock(&ads->out.lock); return rc; } static long tegra_spdif_out_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int rc = 0; struct audio_driver_state *ads = ads_from_misc_out_ctl(file); struct audio_stream *aos = &ads->out; mutex_lock(&aos->lock); switch (cmd) { case TEGRA_AUDIO_OUT_FLUSH: if (pending_buffer_requests(aos)) { pr_debug("%s: flushing\n", __func__); request_stop_nosync(aos); pr_debug("%s: flushed\n", __func__); } if (stop_playback_if_necessary(aos)) pr_debug("%s: done (stopped)\n", __func__); aos->stop = false; break; case TEGRA_AUDIO_OUT_SET_NUM_BUFS: { unsigned int num; if (copy_from_user(&num, (const void __user *)arg, sizeof(num))) { rc = -EFAULT; break; } if (!num || num > SPDIF_MAX_NUM_BUFS) { pr_err("%s: invalid buffer count %d\n", __func__, num); rc = -EINVAL; break; } if (pending_buffer_requests(aos)) { pr_err("%s: playback in progress\n", __func__); rc = -EBUSY; break; } rc = init_stream_buffer(aos, num); if (rc < 0) break; aos->num_bufs = num; sound_ops->setup(ads); } break; case TEGRA_AUDIO_OUT_GET_NUM_BUFS: if (copy_to_user((void __user *)arg, &aos->num_bufs, sizeof(aos->num_bufs))) rc = -EFAULT; break; default: rc = -EINVAL; } mutex_unlock(&aos->lock); return rc; } static int tegra_spdif_out_open(struct inode *inode, struct file *file) { int rc = 0; int i; struct audio_driver_state *ads = ads_from_misc_out(file); pr_debug("%s\n", __func__); mutex_lock(&ads->out.lock); if (ads->out.opened) { rc = -EBUSY; goto done; } ads->out.opened = 1; ads->out.stop = false; for (i = 0; i < SPDIF_MAX_NUM_BUFS; i++) { init_completion(&ads->out.comp[i]); /* TX buf rest state is unqueued, complete. */ complete(&ads->out.comp[i]); } done: mutex_unlock(&ads->out.lock); return rc; } static int tegra_spdif_out_release(struct inode *inode, struct file *file) { struct audio_driver_state *ads = ads_from_misc_out(file); pr_debug("%s\n", __func__); mutex_lock(&ads->out.lock); ads->out.opened = 0; request_stop_nosync(&ads->out); if (stop_playback_if_necessary(&ads->out)) pr_debug("%s: done (stopped)\n", __func__); allow_suspend(&ads->out); mutex_unlock(&ads->out.lock); pr_debug("%s: done\n", __func__); return 0; } static const struct file_operations tegra_spdif_out_fops = { .owner = THIS_MODULE, .open = tegra_spdif_out_open, .release = tegra_spdif_out_release, .write = tegra_spdif_write, }; static int tegra_spdif_ctl_open(struct inode *inode, struct file *file) { return 0; } static int tegra_spdif_ctl_release(struct inode *inode, struct file *file) { return 0; } static const struct file_operations tegra_spdif_out_ctl_fops = { .owner = THIS_MODULE, .open = tegra_spdif_ctl_open, .release = tegra_spdif_ctl_release, .unlocked_ioctl = tegra_spdif_out_ioctl, }; static int init_stream_buffer(struct audio_stream *s, int num) { int i, j; pr_debug("%s (num %d)\n", __func__, num); for (i = 0; i < num; i++) { kfree(s->buffer[i]); s->buffer[i] = kmalloc(buf_size(s), GFP_KERNEL | GFP_DMA); if (!s->buffer[i]) { pr_err("%s: could not allocate buffer\n", __func__); for (j = i - 1; j >= 0; j--) { kfree(s->buffer[j]); s->buffer[j] = 0; } return -ENOMEM; } } return 0; } static int setup_misc_device(struct miscdevice *misc, const struct file_operations *fops, const char *fmt, ...) { int rc = 0; va_list args; const int sz = 64; va_start(args, fmt); memset(misc, 0, sizeof(*misc)); misc->minor = MISC_DYNAMIC_MINOR; misc->name = kmalloc(sz, GFP_KERNEL); if (!misc->name) { rc = -ENOMEM; goto done; } vsnprintf((char *)misc->name, sz, fmt, args); misc->fops = fops; if (misc_register(misc)) { pr_err("%s: could not register %s\n", __func__, misc->name); kfree(misc->name); rc = -EIO; goto done; } done: va_end(args); return rc; } static ssize_t dma_toggle_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "dma\n"); } static ssize_t dma_toggle_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { pr_err("%s: Not implemented.", __func__); return 0; } static DEVICE_ATTR(dma_toggle, 0644, dma_toggle_show, dma_toggle_store); static ssize_t __attr_fifo_atn_read(char *buf, int atn_lvl) { switch (atn_lvl) { case SPDIF_FIFO_ATN_LVL_ONE_SLOT: strncpy(buf, "1\n", 2); return 2; case SPDIF_FIFO_ATN_LVL_FOUR_SLOTS: strncpy(buf, "4\n", 2); return 2; case SPDIF_FIFO_ATN_LVL_EIGHT_SLOTS: strncpy(buf, "8\n", 2); return 2; case SPDIF_FIFO_ATN_LVL_TWELVE_SLOTS: strncpy(buf, "12\n", 3); return 3; default: BUG_ON(1); return -EIO; } } static ssize_t __attr_fifo_atn_write(struct audio_driver_state *ads, struct audio_stream *as, int *fifo_lvl, const char *buf, size_t size) { int lvl; if (size > 3) { pr_err("%s: buffer size %d too big\n", __func__, size); return -EINVAL; } if (sscanf(buf, "%d", &lvl) != 1) { pr_err("%s: invalid input string [%s]\n", __func__, buf); return -EINVAL; } switch (lvl) { case 1: lvl = SPDIF_FIFO_ATN_LVL_ONE_SLOT; break; case 4: lvl = SPDIF_FIFO_ATN_LVL_FOUR_SLOTS; break; case 8: lvl = SPDIF_FIFO_ATN_LVL_EIGHT_SLOTS; break; case 12: lvl = SPDIF_FIFO_ATN_LVL_TWELVE_SLOTS; break; default: pr_err("%s: invalid attention level %d\n", __func__, lvl); return -EINVAL; } *fifo_lvl = lvl; pr_info("%s: fifo level %d\n", __func__, *fifo_lvl); return size; } static ssize_t tx_fifo_atn_show(struct device *dev, struct device_attribute *attr, char *buf) { struct tegra_audio_platform_data *pdata = dev->platform_data; struct audio_driver_state *ads = pdata->driver_data; return __attr_fifo_atn_read(buf, ads->out.spdif_fifo_atn_level); } static ssize_t tx_fifo_atn_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { ssize_t rc; struct tegra_audio_platform_data *pdata = dev->platform_data; struct audio_driver_state *ads = pdata->driver_data; mutex_lock(&ads->out.lock); if (pending_buffer_requests(&ads->out)) { pr_err("%s: playback in progress.\n", __func__); rc = -EBUSY; goto done; } rc = __attr_fifo_atn_write(ads, &ads->out, &ads->out.spdif_fifo_atn_level, buf, count); done: mutex_unlock(&ads->out.lock); return rc; } static DEVICE_ATTR(tx_fifo_atn, 0644, tx_fifo_atn_show, tx_fifo_atn_store); static int spdif_configure(struct platform_device *pdev) { struct tegra_audio_platform_data *pdata = pdev->dev.platform_data; struct audio_driver_state *state = pdata->driver_data; if (!state) return -ENOMEM; set_spdif_clock(state, 44100); spdif_initialize(state->spdif_base, AUDIO_TX_MODE); spdif_fifo_set_attention_level(state->spdif_base, AUDIO_TX_MODE, state->out.spdif_fifo_atn_level); spdif_set_sample_rate(state->spdif_base, 44100); state->fifo_init = true; return 0; } static int tegra_spdif_probe(struct platform_device *pdev) { int rc, i; struct resource *res; struct audio_driver_state *state; pr_info("%s\n", __func__); state = kzalloc(sizeof(*state), GFP_KERNEL); if (!state) return -ENOMEM; state->pdev = pdev; state->pdata = pdev->dev.platform_data; state->pdata->driver_data = state; BUG_ON(!state->pdata); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "no mem resource!\n"); return -ENODEV; } if (!request_mem_region(res->start, resource_size(res), pdev->name)) { dev_err(&pdev->dev, "memory region already claimed!\n"); return -ENOMEM; } state->spdif_phys = res->start; state->spdif_base = (unsigned long)ioremap(res->start, res->end - res->start + 1); if (!state->spdif_base) { dev_err(&pdev->dev, "cannot remap iomem!\n"); return -EIO; } res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!res) { dev_err(&pdev->dev, "no dma resource!\n"); return -ENODEV; } state->dma_req_sel = res->start; #if defined(CONFIG_ARCH_TEGRA_2x_SOC) res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { dev_err(&pdev->dev, "no irq resource!\n"); return -ENODEV; } state->irq = res->start; #endif rc = spdif_configure(pdev); if (rc < 0) return rc; state->out.opened = 0; state->out.active = false; mutex_init(&state->out.lock); init_completion(&state->out.stop_completion); spin_lock_init(&state->out.dma_req_lock); state->out.dma_chan = NULL; state->out.num_bufs = SPDIF_DEFAULT_TX_NUM_BUFS; for (i = 0; i < SPDIF_MAX_NUM_BUFS; i++) { init_completion(&state->out.comp[i]); /* TX buf rest state is unqueued, complete. */ complete(&state->out.comp[i]); state->out.buffer[i] = 0; state->out.buf_phy[i] = 0; } state->out.last_queued = 0; rc = init_stream_buffer(&state->out, state->out.num_bufs); if (rc < 0) return rc; INIT_WORK(&state->out.allow_suspend_work, allow_suspend_worker); snprintf(state->out.wake_lock_name, sizeof(state->out.wake_lock_name), "tegra-audio-spdif"); wake_lock_init(&state->out.wake_lock, WAKE_LOCK_SUSPEND, state->out.wake_lock_name); #if defined(CONFIG_ARCH_TEGRA_2x_SOC) if (request_irq(state->irq, spdif_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; } #endif rc = setup_misc_device(&state->misc_out, &tegra_spdif_out_fops, "spdif_out"); if (rc < 0) return rc; rc = setup_misc_device(&state->misc_out_ctl, &tegra_spdif_out_ctl_fops, "spdif_out_ctl"); if (rc < 0) return rc; sound_ops->setup(state); rc = device_create_file(&pdev->dev, &dev_attr_dma_toggle); if (rc < 0) { dev_err(&pdev->dev, "%s: could not create sysfs entry %s: %d\n", __func__, dev_attr_dma_toggle.attr.name, rc); return rc; } rc = device_create_file(&pdev->dev, &dev_attr_tx_fifo_atn); if (rc < 0) { dev_err(&pdev->dev, "%s: could not create sysfs entry %s: %d\n", __func__, dev_attr_tx_fifo_atn.attr.name, rc); return rc; } return 0; } #ifdef CONFIG_PM static int tegra_spdif_suspend(struct platform_device *pdev, pm_message_t mesg) { /* dev_info(&pdev->dev, "%s\n", __func__); */ return 0; } static int tegra_spdif_resume(struct platform_device *pdev) { return spdif_configure(pdev); } #endif /* CONFIG_PM */ static struct platform_driver tegra_spdif_driver = { .driver = { .name = "spdif_out", .owner = THIS_MODULE, }, .probe = tegra_spdif_probe, #ifdef CONFIG_PM .suspend = tegra_spdif_suspend, .resume = tegra_spdif_resume, #endif }; static int __init tegra_spdif_init(void) { return platform_driver_register(&tegra_spdif_driver); } module_init(tegra_spdif_init); MODULE_LICENSE("GPL");