/* * Configures McPDM downlink for audio playback. * This function should be called before omap_mcpdm_start. */ int omap_mcpdm_playback_open(struct omap_mcpdm_link *downlink) { int irq_mask = 0; int ctrl; if (!downlink) return -EINVAL; mcpdm->downlink = downlink; /* Enable irq request generation */ irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK; omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask); /* Configure uplink threshold */ if (downlink->threshold > DN_THRES_MAX) downlink->threshold = DN_THRES_MAX; omap_mcpdm_write(MCPDM_FIFO_CTRL_DN, downlink->threshold); /* Enable DMA request generation */ omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_DN_ENABLE); /* Set pdm out format */ ctrl = omap_mcpdm_read(MCPDM_CTRL); ctrl &= ~PDMOUTFORMAT; ctrl |= downlink->format & PDMOUTFORMAT; /* Downlink channels */ mcpdm->dn_channels = downlink->channels & (PDM_DN_MASK | PDM_CMD_MASK); omap_mcpdm_write(MCPDM_CTRL, ctrl); return 0; }
/* * Configures McPDM uplink for audio recording. * This function should be called before omap_mcpdm_start. */ int omap_mcpdm_capture_open(struct omap_mcpdm_link *uplink) { int irq_mask = 0; int ctrl; if (!uplink) return -EINVAL; mcpdm->uplink = uplink; /* Enable irq request generation */ irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK; omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask); /* Configure uplink threshold */ if (uplink->threshold > UP_THRES_MAX) uplink->threshold = UP_THRES_MAX; omap_mcpdm_write(MCPDM_FIFO_CTRL_UP, uplink->threshold); /* Configure DMA controller */ omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_UP_ENABLE); /* Set pdm out format */ ctrl = omap_mcpdm_read(MCPDM_CTRL); ctrl &= ~PDMOUTFORMAT; ctrl |= uplink->format & PDMOUTFORMAT; /* Uplink channels */ mcpdm->up_channels = uplink->channels & (PDM_UP_MASK | PDM_STATUS_MASK); omap_mcpdm_write(MCPDM_CTRL, ctrl); return 0; }
/* * Enables the transfer through the PDM interface to/from the Phoenix * codec by enabling the corresponding UP or DN channels. */ static void omap_mcpdm_start(struct omap_mcpdm *mcpdm) { u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL); u32 link_mask = mcpdm->config[0].link_mask | mcpdm->config[1].link_mask; ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); ctrl |= link_mask; omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); }
/* * Disables the transfer through the PDM interface to/from the Phoenix * codec by disabling the corresponding UP or DN channels. */ static void omap_mcpdm_stop(struct omap_mcpdm *mcpdm) { u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL); u32 link_mask = MCPDM_PDM_DN_MASK | MCPDM_PDM_UP_MASK; ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); ctrl &= ~(link_mask); omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); }
static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id) { struct omap_mcpdm *mcpdm_irq = dev_id; int irq_status; irq_status = omap_mcpdm_read(MCPDM_IRQSTATUS); /* Acknowledge irq event */ omap_mcpdm_write(MCPDM_IRQSTATUS, irq_status); switch (irq_status) { case DN_IRQ_FULL: case DN_IRQ_EMTPY: dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status); omap_mcpdm_reset(MCPDM_DOWNLINK, 1); omap_mcpdm_set_downlink(mcpdm_irq->downlink); omap_mcpdm_reset(MCPDM_DOWNLINK, 0); break; case DN_IRQ: dev_dbg(mcpdm_irq->dev, "DN write request\n"); break; case UP_IRQ_FULL: case UP_IRQ_EMPTY: dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status); omap_mcpdm_reset(MCPDM_UPLINK, 1); omap_mcpdm_set_uplink(mcpdm_irq->uplink); omap_mcpdm_reset(MCPDM_UPLINK, 0); break; case UP_IRQ: dev_dbg(mcpdm_irq->dev, "UP write request\n"); break; } return IRQ_HANDLED; }
static int omap_mcpdm_probe(struct snd_soc_dai *dai) { struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); int ret; pm_runtime_enable(mcpdm->dev); /* Disable lines while request is ongoing */ pm_runtime_get_sync(mcpdm->dev); omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, 0x00); ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler, 0, "McPDM", (void *)mcpdm); pm_runtime_put_sync(mcpdm->dev); if (ret) { dev_err(mcpdm->dev, "Request for IRQ failed\n"); pm_runtime_disable(mcpdm->dev); } /* Configure McPDM threshold values */ mcpdm->config[SNDRV_PCM_STREAM_PLAYBACK].threshold = 2; mcpdm->config[SNDRV_PCM_STREAM_CAPTURE].threshold = MCPDM_UP_THRES_MAX - 3; snd_soc_dai_init_dma_data(dai, &mcpdm->dma_data[SNDRV_PCM_STREAM_PLAYBACK], &mcpdm->dma_data[SNDRV_PCM_STREAM_CAPTURE]); return ret; }
static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id) { struct omap_mcpdm *mcpdm = dev_id; int irq_status; irq_status = omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS); /* Acknowledge irq event */ omap_mcpdm_write(mcpdm, MCPDM_REG_IRQSTATUS, irq_status); if (irq_status & MCPDM_DN_IRQ_FULL) dev_dbg(mcpdm->dev, "DN (playback) FIFO Full\n"); if (irq_status & MCPDM_DN_IRQ_EMPTY) dev_dbg(mcpdm->dev, "DN (playback) FIFO Empty\n"); if (irq_status & MCPDM_DN_IRQ) dev_dbg(mcpdm->dev, "DN (playback) write request\n"); if (irq_status & MCPDM_UP_IRQ_FULL) dev_dbg(mcpdm->dev, "UP (capture) FIFO Full\n"); if (irq_status & MCPDM_UP_IRQ_EMPTY) dev_dbg(mcpdm->dev, "UP (capture) FIFO Empty\n"); if (irq_status & MCPDM_UP_IRQ) dev_dbg(mcpdm->dev, "UP (capture) write request\n"); return IRQ_HANDLED; }
/* Enable/disable DC offset cancelation for the analog * headset path (PDM channels 1 and 2). */ int omap_mcpdm_set_offset(int offset1, int offset2) { int offset; if ((offset1 > DN_OFST_MAX) || (offset2 > DN_OFST_MAX)) return -EINVAL; offset = (offset1 << DN_OFST_RX1) | (offset2 << DN_OFST_RX2); /* offset cancellation for channel 1 */ if (offset1) offset |= DN_OFST_RX1_EN; else offset &= ~DN_OFST_RX1_EN; /* offset cancellation for channel 2 */ if (offset2) offset |= DN_OFST_RX2_EN; else offset &= ~DN_OFST_RX2_EN; omap_mcpdm_write(MCPDM_DN_OFFSET, offset); return 0; }
int omap_mcpdm_request(void) { int ret; clk_enable(mcpdm->clk); spin_lock(&mcpdm->lock); if (!mcpdm->free) { dev_err(mcpdm->dev, "McPDM interface is in use\n"); spin_unlock(&mcpdm->lock); ret = -EBUSY; goto err; } mcpdm->free = 0; spin_unlock(&mcpdm->lock); /* Disable lines while request is ongoing */ omap_mcpdm_write(MCPDM_CTRL, 0x00); ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler, 0, "McPDM", (void *)mcpdm); if (ret) { dev_err(mcpdm->dev, "Request for McPDM IRQ failed\n"); goto err; } return 0; err: clk_disable(mcpdm->clk); return ret; }
/* * Configures McPDM uplink, and downlink for audio. * This function should be called before omap_mcpdm_start. */ static void omap_mcpdm_open_streams(struct omap_mcpdm *mcpdm) { u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL); omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl | MCPDM_WD_EN); omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_SET, MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL | MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL); /* Enable DN RX1/2 offset cancellation feature, if configured */ if (mcpdm->dn_rx_offset) { u32 dn_offset = mcpdm->dn_rx_offset; omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, dn_offset); dn_offset |= (MCPDM_DN_OFST_RX1_EN | MCPDM_DN_OFST_RX2_EN); omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, dn_offset); } omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_DN, mcpdm->config[SNDRV_PCM_STREAM_PLAYBACK].threshold); omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_UP, mcpdm->config[SNDRV_PCM_STREAM_CAPTURE].threshold); omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_SET, MCPDM_DMA_DN_ENABLE | MCPDM_DMA_UP_ENABLE); }
/* * Cleans McPDM uplink, and downlink configuration. * This function should be called when the stream is closed. */ static void omap_mcpdm_close_streams(struct omap_mcpdm *mcpdm) { /* Disable irq request generation for downlink */ omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_CLR, MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL); /* Disable DMA request generation for downlink */ omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_CLR, MCPDM_DMA_DN_ENABLE); /* Disable irq request generation for uplink */ omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_CLR, MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL); /* Disable DMA request generation for uplink */ omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_CLR, MCPDM_DMA_UP_ENABLE); /* Disable RX1/2 offset cancellation */ if (mcpdm->dn_rx_offset) omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, 0); }
/* * Disables the transfer through the PDM interface to/from the Phoenix * codec by disabling the corresponding UP or DN channels. */ void omap_mcpdm_stop(int stream) { int ctrl = omap_mcpdm_read(MCPDM_CTRL); if (stream) ctrl &= ~mcpdm->up_channels; else ctrl &= ~mcpdm->dn_channels; omap_mcpdm_write(MCPDM_CTRL, ctrl); }
static void omap_mcpdm_reset_playback(int reset) { int ctrl = omap_mcpdm_read(MCPDM_CTRL); if (reset) ctrl |= SW_DN_RST; else ctrl &= ~SW_DN_RST; omap_mcpdm_write(MCPDM_CTRL, ctrl); }
/* * Takes the McPDM module in and out of reset state. * Uplink and downlink can be reset individually. */ static void omap_mcpdm_reset_capture(int reset) { int ctrl = omap_mcpdm_read(MCPDM_CTRL); if (reset) ctrl |= SW_UP_RST; else ctrl &= ~SW_UP_RST; omap_mcpdm_write(MCPDM_CTRL, ctrl); }
/* * Cleans McPDM uplink configuration. * This function should be called when the stream is closed. */ int omap_mcpdm_capture_close(struct omap_mcpdm_link *uplink) { int irq_mask = 0; if (!uplink) return -EINVAL; /* Disable irq request generation */ irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK; omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask); /* Disable DMA request generation */ omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_UP_ENABLE); /* Clear Downlink channels */ mcpdm->up_channels = 0; mcpdm->uplink = NULL; return 0; }
/* * Cleans McPDM downlink configuration. * This function should be called when the stream is closed. */ int omap_mcpdm_playback_close(struct omap_mcpdm_link *downlink) { int irq_mask = 0; if (!downlink) return -EINVAL; /* Disable irq request generation */ irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK; omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask); /* Disable DMA request generation */ omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_DN_ENABLE); /* clear Downlink channels */ mcpdm->dn_channels = 0; mcpdm->downlink = NULL; return 0; }
static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id) { struct omap_mcpdm *mcpdm_irq = dev_id; int irq_status; irq_status = omap_mcpdm_read(MCPDM_IRQSTATUS); /* Acknowledge irq event */ omap_mcpdm_write(MCPDM_IRQSTATUS, irq_status); if (irq & MCPDM_DN_IRQ_FULL) { dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status); omap_mcpdm_reset_playback(1); omap_mcpdm_playback_open(mcpdm_irq->downlink); omap_mcpdm_reset_playback(0); } if (irq & MCPDM_DN_IRQ_EMPTY) { dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status); omap_mcpdm_reset_playback(1); omap_mcpdm_playback_open(mcpdm_irq->downlink); omap_mcpdm_reset_playback(0); } if (irq & MCPDM_DN_IRQ) { dev_dbg(mcpdm_irq->dev, "DN write request\n"); } if (irq & MCPDM_UP_IRQ_FULL) { dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status); omap_mcpdm_reset_capture(1); omap_mcpdm_capture_open(mcpdm_irq->uplink); omap_mcpdm_reset_capture(0); } if (irq & MCPDM_UP_IRQ_EMPTY) { dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status); omap_mcpdm_reset_capture(1); omap_mcpdm_capture_open(mcpdm_irq->uplink); omap_mcpdm_reset_capture(0); } if (irq & MCPDM_UP_IRQ) { dev_dbg(mcpdm_irq->dev, "UP write request\n"); } return IRQ_HANDLED; }
static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); mutex_lock(&mcpdm->mutex); if (!dai->active) { u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL); omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl | MCPDM_WD_EN); omap_mcpdm_open_streams(mcpdm); } mutex_unlock(&mcpdm->mutex); snd_soc_dai_set_dma_data(dai, substream, &mcpdm->dma_data[substream->stream]); return 0; }
void omap_mcpdm_reset(int links, int reset) { int ctrl = omap_mcpdm_read(MCPDM_CTRL); if (links & MCPDM_UPLINK) { if (reset) ctrl |= SW_UP_RST; else ctrl &= ~SW_UP_RST; } if (links & MCPDM_DOWNLINK) { if (reset) ctrl |= SW_DN_RST; else ctrl &= ~SW_DN_RST; } omap_mcpdm_write(MCPDM_CTRL, ctrl); }