| /* |
| * linux/sound/soc/pxa/pxa-sndcard.c |
| * |
| * Copyright (C) 2013 Marvell International Ltd. |
| * |
| * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| * |
| */ |
| #include <linux/module.h> |
| #include <linux/moduleparam.h> |
| #include <linux/interrupt.h> |
| #include <linux/platform_device.h> |
| #include <linux/mfd/88pm8xxx-headset.h> |
| #include <linux/features.h> |
| #include <sound/core.h> |
| #include <sound/pcm.h> |
| #include <sound/soc.h> |
| #include <sound/jack.h> |
| #include <linux/io.h> |
| #include <linux/uaccess.h> |
| #include <sound/pcm_params.h> |
| #include <linux/pxa2xx_ssp.h> |
| #include <linux/input.h> |
| #include "pxa-ssp.h" |
| |
| #ifdef CONFIG_SND_PXA_SSP_DUMP |
| int ssp_playback_enable; |
| int ssp_capture_enable; |
| int gssp_playback_enable; |
| int gssp_capture_enable; |
| #endif |
| |
| static int gssp_master = 1; |
| |
| /* CODEC I2S config is Master in config/defconfig_asr1802sp201 */ |
| #ifdef CONFIG_CODEC_I2S_SLAVE |
| |
| /* CODEC is Slave, SSP is Master. */ |
| static int ssp_master = 1; |
| |
| #else |
| |
| /* CODEC is Master, SSP is slave. */ |
| static int ssp_master = 0; //0: PXA SSP as slave, hardware codec as master |
| |
| #endif |
| |
| |
| u32 top_ctrl_hifi; |
| u32 fifo_ctrl_hifi; |
| u32 rwot_ctrl_hifi; |
| u32 network_ctrl_hifi; |
| u32 int_en_hifi; |
| |
| /* format for ASR1802SL */ |
| unsigned int channels; |
| unsigned int rate; |
| unsigned int sample_bits; |
| unsigned int frame_bits; |
| |
| /* |
| * SSP audio private data |
| */ |
| struct ssp_priv { |
| struct ssp_device *ssp; |
| unsigned int sysclk; |
| int dai_fmt; |
| #ifdef CONFIG_PM |
| uint32_t cr0; |
| uint32_t cr1; |
| uint32_t to; |
| uint32_t psp; |
| #endif |
| }; |
| |
| extern int get_frame_bits(void); |
| extern int get_status_switch_frame_bits(void); |
| extern int get_status_IFS(void); |
| extern int get_status_SFRMP(void); |
| extern int get_status_SCMODE(void); |
| extern u32 get_reg_value_from_UI(int offset); |
| extern int get_status_debug_tools(void); |
| |
| int get_ssp_role(void) |
| { |
| return ssp_master; |
| } |
| |
| void set_ssp_role(int role) |
| { |
| ssp_master = role; |
| printk(KERN_INFO"%s: SSP1 role is %s.\n", __FUNCTION__, ssp_master?"Master":"Slave"); |
| return; |
| } |
| |
| static ssize_t ssp_master_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return sprintf(buf, "%s\n", ssp_master?"SSP is Master!":"SSP is Slave!"); |
| } |
| |
| static ssize_t ssp_master_set(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| int ret; |
| |
| ret = kstrtoint(buf, 10, &ssp_master); |
| if (ret) |
| return ret; |
| |
| return count; |
| } |
| |
| static DEVICE_ATTR(ssp_master, 0644, ssp_master_show, ssp_master_set); |
| |
| static ssize_t gssp_master_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return sprintf(buf, "%d\n", gssp_master); |
| } |
| |
| static ssize_t gssp_master_set(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| int ret; |
| |
| ret = kstrtoint(buf, 10, &gssp_master); |
| if (ret) |
| return ret; |
| |
| return count; |
| } |
| |
| static DEVICE_ATTR(gssp_master, 0644, gssp_master_show, gssp_master_set); |
| |
| static ssize_t gssp_record_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return sprintf(buf, "%d\n", has_feat_hifi_gssp_record()); |
| } |
| |
| static DEVICE_ATTR(gssp_record, 0444, gssp_record_show, NULL); |
| |
| #ifdef CONFIG_SND_PXA_SSP_DUMP |
| static DEVICE_INT_ATTR(ssp_playback_dump, 0644, ssp_playback_enable); |
| static DEVICE_INT_ATTR(ssp_capture_dump, 0644, ssp_capture_enable); |
| static DEVICE_INT_ATTR(gssp_playback_dump, 0644, gssp_playback_enable); |
| static DEVICE_INT_ATTR(gssp_capture_dump, 0644, gssp_capture_enable); |
| #endif |
| |
| static int pxa_sndcard_hifi_startup(struct snd_pcm_substream *substream) |
| { |
| return 0; |
| } |
| |
| static int pxa_sndcard_hw_params(struct snd_pcm_substream *substream, |
| struct snd_pcm_hw_params *params) |
| { |
| struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| struct snd_soc_dai *cpu_dai = rtd->cpu_dai; |
| struct snd_soc_dai *codec_dai = rtd->codec_dai; |
| |
| channels = params_channels(params); |
| rate = params_rate(params); |
| sample_bits = snd_pcm_format_physical_width(params_format(params)); |
| frame_bits = sample_bits * channels; |
| |
| if (get_status_switch_frame_bits()) |
| { |
| /* for debug */ |
| frame_bits = get_frame_bits(); |
| } else { |
| /* get frame length from file format */ |
| frame_bits = sample_bits * channels; |
| } |
| |
| if (ssp_master) |
| { |
| snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | |
| SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); |
| snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | |
| SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); |
| } |
| else |
| { |
| snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | |
| SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); |
| snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | |
| SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); |
| } |
| /*mclk is input clock of codec, it depends on codec configuration |
| *transfered by callback function |
| */ |
| snd_soc_dai_set_sysclk(codec_dai, 1, rate, SND_SOC_CLOCK_IN); |
| return 0; |
| } |
| extern void ssp_dump_registers(struct ssp_device *ssp); |
| extern struct ssp_device *g_ssp; |
| |
| static int pxa_sndcard_hifi_prepare(struct snd_pcm_substream *substream) |
| { |
| struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| struct snd_soc_dai *cpu_dai = rtd->cpu_dai; |
| struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); |
| struct ssp_device *ssp = priv->ssp; |
| |
| u32 top_ctrl, fifo_ctrl, rwot_ctrl, network_ctrl, psp_ctrl, int_en; |
| int scmode, status_debug_tool; |
| |
| if (has_feat_hifi_gssp_record()) { |
| |
| if(ssp_master) { |
| /* master mmode, drive SCLK and SFRM */ |
| top_ctrl_hifi = top_ctrl_hifi & (~0x18); |
| /* Transmit/Receive service request enable |
| * TXFIFO/RXFIFO threshold as 0x8 |
| */ |
| fifo_ctrl_hifi = fifo_ctrl_hifi | 0xCE7; //RX_AUTO_FULL_CTRL_EN (0x1<<17)??? |
| /* Receive without transmit */ |
| //rwot_ctrl_hifi = rwot_ctrl_hifi | 0x1; |
| } else { |
| /* slave mmode, receive SCLK and SFRM */ |
| top_ctrl_hifi = top_ctrl_hifi | 0x18; |
| /* Transmit/Receive service request enable |
| * TXFIFO/RXFIFO threshold as 0x8 |
| */ |
| fifo_ctrl_hifi = fifo_ctrl_hifi | 0xCE7; //RX_AUTO_FULL_CTRL_EN (0x1<<17)??? |
| /* Receive without transmit */ |
| //rwot_ctrl_hifi = rwot_ctrl_hifi | 0x1; |
| } |
| |
| /* Frame Rate Divider Ctrl */ |
| network_ctrl_hifi = network_ctrl_hifi | (1 << 1); |
| /* TIM/RIM */ |
| int_en_hifi = int_en_hifi | (0x3 << 4); |
| /* 32bit data, Frame format as PSP */ |
| top_ctrl_hifi = top_ctrl_hifi | (0x1f << 5) | (0x3 << 1); |
| __raw_writel(top_ctrl_hifi, ssp->mmio_base + TOP_CTRL); |
| __raw_writel(fifo_ctrl_hifi, ssp->mmio_base + FIFO_CTRL); |
| __raw_writel(rwot_ctrl_hifi, ssp->mmio_base + RWOT_CTRL); |
| __raw_writel(int_en_hifi, ssp->mmio_base + INT_EN); |
| __raw_writel(network_ctrl_hifi, ssp->mmio_base + NET_WORK_CTRL); |
| __raw_writel(0x00010018, ssp->mmio_base + PSP_CTRL); |
| |
| } else { |
| |
| top_ctrl = __raw_readl(ssp->mmio_base + TOP_CTRL); |
| fifo_ctrl = __raw_readl(ssp->mmio_base + FIFO_CTRL); |
| rwot_ctrl = __raw_readl(ssp->mmio_base + RWOT_CTRL); |
| int_en = __raw_readl(ssp->mmio_base + INT_EN); |
| network_ctrl = __raw_readl(ssp->mmio_base + NET_WORK_CTRL); |
| |
| if(ssp_master){ |
| /* ssp is Master */ |
| #if 1 |
| //deleted for debug |
| top_ctrl = 0; |
| fifo_ctrl = 0; |
| int_en = 0; |
| psp_ctrl = 0; |
| network_ctrl = 0; |
| scmode = get_status_SCMODE(); |
| status_debug_tool = get_status_debug_tools(); |
| //rwot_ctrl = 0; |
| |
| //Register:TOP_CTRL[0x00] |
| /* TXD Three-State Enable */ |
| top_ctrl = top_ctrl | (0x1 << 17); |
| |
| /* |
| *One:adjust polarity between codec bit[3:0] of register[0x32] in acm_pm805.h and ssp here |
| *IFS: bit15[TOP_CTRL] |
| */ |
| if (get_status_IFS()) { |
| top_ctrl = top_ctrl | (0x01 << 15); |
| } else { |
| top_ctrl &= ~(0x01 << 15); |
| } |
| |
| /* TRAIL:bit13 DMA bursts */ |
| top_ctrl = top_ctrl | (0x1 << 13); |
| |
| if (64 == frame_bits) { |
| /* 32bit data size, Frame format as PSP */ |
| top_ctrl = top_ctrl | (0x1f << 5); |
| } else if (32 == frame_bits) { |
| /* 32bit data size, Frame format as PSP */ |
| top_ctrl = top_ctrl | (0x1f << 5); |
| } else if (16 == frame_bits) { |
| /* 16bit data size, Frame format as PSP */ |
| top_ctrl = top_ctrl | (0x0f << 5); |
| } |
| |
| /* slave mode, receive SCLK and SFRM */ |
| //top_ctrl = top_ctrl | (0x3 << 3); |
| /* master mode, receive SCLK and SFRM */ |
| top_ctrl &= ~(0x3 << 3); |
| |
| /* Frame format as PSP */ |
| top_ctrl = top_ctrl | (0x3 << 1); |
| |
| if (status_debug_tool) { |
| __raw_writel(get_reg_value_from_UI(TOP_CTRL), ssp->mmio_base + TOP_CTRL); |
| } else { |
| __raw_writel(top_ctrl, ssp->mmio_base + TOP_CTRL); |
| } |
| #if 1 |
| /* |
| *Register:FIFO_CTRL[0x04] |
| *Transmit/Receive service request enable |
| *TXFIFO/RXFIFO threshold as 0x8 |
| *to avoid interrupt triggering |
| *when ssp configured for transporting 48kHz data |
| */ |
| |
| //fifo_ctrl = fifo_ctrl | (0x1 << 16); |
| fifo_ctrl = fifo_ctrl | (0x3 << 10); |
| |
| /*RX FIFO Trigger Threshold*/ |
| fifo_ctrl = fifo_ctrl | (0x7 << 5); |
| |
| /*TX FIFO Trigger Threshold*/ |
| fifo_ctrl = fifo_ctrl | (0x7 << 0); |
| |
| if (status_debug_tool) { |
| __raw_writel(get_reg_value_from_UI(FIFO_CTRL), ssp->mmio_base + FIFO_CTRL); |
| } else { |
| __raw_writel(fifo_ctrl, ssp->mmio_base + FIFO_CTRL); |
| } |
| #else |
| /* test2 */ |
| fifo_ctrl = fifo_ctrl | (0x1 << 17); /* RX_AUTO_FULL_CTRL_EN */ |
| fifo_ctrl &= ~(0x1 << 16);/* FIFO_UNPACKING */ |
| fifo_ctrl = fifo_ctrl | (0x1 << 11);/* RX_REQ_EN */ |
| fifo_ctrl = fifo_ctrl | (0x1 << 10);/* TX_REQ_EN */ |
| fifo_ctrl = fifo_ctrl | (0x7 << 5);/* RX_THRES_7 */ |
| fifo_ctrl = fifo_ctrl | (0x7 << 0);/* TX_THRES_7 */ |
| __raw_writel(fifo_ctrl, ssp->mmio_base + FIFO_CTRL); |
| #endif |
| |
| #if 0 |
| __raw_writel(int_en, ssp->mmio_base + INT_EN); |
| #else |
| /* test3 */ |
| //int_en = int_en | (0x1 << 3);/* TX_FIFO_INT_EN */ |
| //int_en = int_en | (0x1 << 2);/* RX_FIFO_INT_EN */ |
| int_en = int_en | (0x1 << 1);/* TIMEOUT_EN */ |
| int_en = int_en | (0x1 << 0);/* PERI_TRAIL_EN */ |
| |
| if (status_debug_tool) { |
| __raw_writel(get_reg_value_from_UI(INT_EN), ssp->mmio_base + INT_EN); |
| } else { |
| __raw_writel(int_en, ssp->mmio_base + INT_EN); |
| } |
| #endif |
| |
| #if 0 |
| __raw_writel(0, ssp->mmio_base + TO); |
| #else |
| /* test4 |
| * set no timeout interrupt here |
| * the interrupt is triggered, which depends on the frequency of the sample rate |
| * If the sample rate is 8KHz, the interrupt is triggered too fast and there is no |
| * interrupt handler to deal with it. |
| */ |
| __raw_writel(0x0, ssp->mmio_base + TO); |
| #endif |
| |
| if (64 == frame_bits) { |
| /* Serial Frame Width:32, SFRMWDTH */ |
| psp_ctrl = psp_ctrl | (0x20 << 12); |
| } else if (32 == frame_bits) { |
| /* Register:PSP_CTRL[0x18] */ |
| /* SFRMWDTH:must be needed when slave */ |
| /* Serial Frame Width:16, SFRMWDTH */ |
| psp_ctrl = psp_ctrl | (0x10 << 12); |
| } else if (16 == frame_bits) { |
| /* Serial Frame Width:8, SFRMWDTH */ |
| psp_ctrl = psp_ctrl | (0x08 << 12); |
| } |
| |
| /* |
| *Two:adjust polarity between codec bit[3:0] of register[0x32] in acm_pm805.h and ssp here |
| *Frame Polarity bit[4] |
| */ |
| /* SFRMP */ |
| //psp_ctrl = psp_ctrl | (0x1 << 4); |
| if (get_status_SFRMP()) { |
| psp_ctrl = psp_ctrl | (0x1 << 4); |
| } else { |
| psp_ctrl &= ~(0x01 << 4); |
| } |
| |
| |
| /* FSRT */ |
| psp_ctrl = psp_ctrl | (0x1 << 3); |
| |
| /* |
| *Three:adjust polarity between codec bit[3:0] of register[0x32] in acm_pm805.h and ssp here |
| *Bitclk Polarity bit[1:0] |
| */ |
| /* SCMODE */ |
| #if 0 |
| //psp_ctrl = psp_ctrl | (0x0 << 0); |
| psp_ctrl = psp_ctrl | (0x1 << 0); |
| //psp_ctrl = psp_ctrl | (0x2 << 0); |
| //psp_ctrl = psp_ctrl | (0x3 << 0); |
| #else |
| //for test |
| if (0x3 == scmode) { |
| psp_ctrl = psp_ctrl | (0x3 << 0); |
| } else if (0x2 == scmode) { |
| psp_ctrl = psp_ctrl | (0x2 << 0); |
| } else if (0x1 == scmode) { |
| psp_ctrl = psp_ctrl | (0x1 << 0); |
| } else if (0x0 == scmode) { |
| psp_ctrl = psp_ctrl | (0x0 << 0); |
| } |
| #endif |
| |
| /* PSP_CTRL[0x18] */ |
| if (status_debug_tool) { |
| __raw_writel(get_reg_value_from_UI(PSP_CTRL), ssp->mmio_base + PSP_CTRL); |
| } else { |
| __raw_writel(psp_ctrl, ssp->mmio_base + PSP_CTRL); |
| } |
| |
| if ((32 == frame_bits) || (16 == frame_bits)) { |
| /* NETWORK_CTRL[0x1C] */ |
| /* FRDC: must be needed when slave */ |
| |
| /* Disable the network mode */ |
| if (status_debug_tool) { |
| __raw_writel(get_reg_value_from_UI(NET_WORK_CTRL), ssp->mmio_base + NET_WORK_CTRL); |
| } else { |
| __raw_writel(0, ssp->mmio_base + NET_WORK_CTRL); |
| } |
| } else if (64 == frame_bits) { |
| /* Enable the network mode */ |
| network_ctrl = network_ctrl | (0x3<<12);/* RX slot time */ |
| network_ctrl = network_ctrl | (0x3<<4);/* TX slot time */ |
| network_ctrl = network_ctrl | (0x1<<1);/* FRDC */ |
| network_ctrl = network_ctrl | (0x1<<0);/* NET_WORK_MOD */ |
| |
| if (status_debug_tool) { |
| __raw_writel(get_reg_value_from_UI(NET_WORK_CTRL), ssp->mmio_base + NET_WORK_CTRL); |
| } else { |
| __raw_writel(network_ctrl, ssp->mmio_base + NET_WORK_CTRL); |
| } |
| } |
| |
| //rwot_ctrl = rwot_ctrl | (1 << 0); |
| //__raw_writel(rwot_ctrl, ssp->mmio_base + RWOT_CTRL); |
| |
| #if 0 |
| top_ctrl |= 1; |
| __raw_writel(top_ctrl, ssp->mmio_base + TOP_CTRL); |
| #endif |
| |
| // printk(KERN_INFO"master: TOP_CTRL 0x%08x FIFO_CTRL 0x%08x PSP_CTRL 0x%08x\n", top_ctrl, fifo_ctrl, psp_ctrl); |
| #endif |
| } else { |
| /* ssp is slave */ |
| #if 1 |
| //deleted for debug |
| top_ctrl = 0; |
| fifo_ctrl = 0; |
| int_en = 0; |
| psp_ctrl = 0; |
| network_ctrl = 0; |
| scmode = get_status_SCMODE(); |
| status_debug_tool = get_status_debug_tools(); |
| //rwot_ctrl = 0; |
| |
| //Register:TOP_CTRL[0x00] |
| |
| /* TXD Three-State Enable */ |
| top_ctrl = top_ctrl | (0x1 << 17); |
| |
| /* |
| *One:adjust polarity between codec bit[3:0] of register[0x32] in acm_pm805.h and ssp here |
| *IFS: bit15[TOP_CTRL] |
| */ |
| if (get_status_IFS()) { |
| top_ctrl = top_ctrl | (0x01 << 15); |
| } else { |
| top_ctrl &= ~(0x01 << 15); |
| } |
| |
| /* TRAIL:bit13 DMA bursts */ |
| top_ctrl = top_ctrl | (0x1 << 13); |
| |
| if (64 == frame_bits) { |
| /* 32bit data size, Frame format as PSP */ |
| top_ctrl = top_ctrl | (0x1f << 5); |
| } else if (32 == frame_bits) { |
| /* 32bit data size, Frame format as PSP */ |
| top_ctrl = top_ctrl | (0x1f << 5); |
| } else if (16 == frame_bits) { |
| /* 16bit data size, Frame format as PSP */ |
| top_ctrl = top_ctrl | (0x0f << 5); |
| } |
| |
| /* slave mode, receive SCLK and SFRM */ |
| top_ctrl = top_ctrl | (0x3 << 3); |
| /* master mode, receive SCLK and SFRM */ |
| //top_ctrl &= ~(0x3 << 3); |
| |
| /* Frame format as PSP */ |
| top_ctrl = top_ctrl | (0x3 << 1); |
| |
| if (status_debug_tool) { |
| __raw_writel(get_reg_value_from_UI(TOP_CTRL), ssp->mmio_base + TOP_CTRL); |
| } else { |
| __raw_writel(top_ctrl, ssp->mmio_base + TOP_CTRL); |
| } |
| #if 1 |
| /* |
| *Register:FIFO_CTRL[0x04] |
| *Transmit/Receive service request enable |
| *TXFIFO/RXFIFO threshold as 0x8 |
| */ |
| |
| //fifo_ctrl = fifo_ctrl | (0x1 << 16); |
| fifo_ctrl = fifo_ctrl | (0x3 << 10); |
| |
| /*RX FIFO Trigger Threshold*/ |
| fifo_ctrl = fifo_ctrl | (0x3 << 5); |
| |
| /*TX FIFO Trigger Threshold*/ |
| fifo_ctrl = fifo_ctrl | (0x3 << 0); |
| |
| if (status_debug_tool) { |
| __raw_writel(get_reg_value_from_UI(FIFO_CTRL), ssp->mmio_base + FIFO_CTRL); |
| } else { |
| __raw_writel(fifo_ctrl, ssp->mmio_base + FIFO_CTRL); |
| } |
| #else |
| /* test2 */ |
| fifo_ctrl = fifo_ctrl | (0x1 << 17); /* RX_AUTO_FULL_CTRL_EN */ |
| fifo_ctrl &= ~(0x1 << 16);/* FIFO_UNPACKING */ |
| fifo_ctrl = fifo_ctrl | (0x1 << 11);/* RX_REQ_EN */ |
| fifo_ctrl = fifo_ctrl | (0x1 << 10);/* TX_REQ_EN */ |
| fifo_ctrl = fifo_ctrl | (0x7 << 5);/* RX_THRES_7 */ |
| fifo_ctrl = fifo_ctrl | (0x7 << 0);/* TX_THRES_7 */ |
| __raw_writel(fifo_ctrl, ssp->mmio_base + FIFO_CTRL); |
| #endif |
| |
| #if 1 |
| __raw_writel(int_en, ssp->mmio_base + INT_EN); |
| #else |
| /* test3 */ |
| int_en = int_en | (0x1 << 3);/* TX_FIFO_INT_EN */ |
| int_en = int_en | (0x1 << 2);/* RX_FIFO_INT_EN */ |
| int_en = int_en | (0x1 << 1);/* TIMEOUT_EN */ |
| int_en = int_en | (0x1 << 0);/* PERI_TRAIL_EN */ |
| |
| if (status_debug_tool) { |
| __raw_writel(get_reg_value_from_UI(INT_EN), ssp->mmio_base + INT_EN); |
| } else { |
| __raw_writel(int_en, ssp->mmio_base + INT_EN); |
| } |
| #endif |
| |
| #if 1 |
| __raw_writel(0, ssp->mmio_base + TO); |
| #else |
| /* test4 */ |
| __raw_writel(0x200, ssp->mmio_base + TO); |
| #endif |
| |
| if (64 == frame_bits) { |
| /* Serial Frame Width:32, SFRMWDTH */ |
| psp_ctrl = psp_ctrl | (0x20 << 12); |
| } else if (32 == frame_bits) { |
| /* Register:PSP_CTRL[0x18] */ |
| /* SFRMWDTH:must be needed when slave */ |
| /* Serial Frame Width:16, SFRMWDTH */ |
| psp_ctrl = psp_ctrl | (0x10 << 12); |
| } else if (16 == frame_bits) { |
| /* Serial Frame Width:8, SFRMWDTH */ |
| psp_ctrl = psp_ctrl | (0x08 << 12); |
| } |
| |
| /* |
| *Two:adjust polarity between codec bit[3:0] of register[0x32] in acm_pm805.h and ssp here |
| *Frame Polarity bit[4] |
| */ |
| /* SFRMP */ |
| //psp_ctrl = psp_ctrl | (0x1 << 4); |
| if (get_status_SFRMP()) { |
| psp_ctrl = psp_ctrl | (0x1 << 4); |
| } else { |
| psp_ctrl &= ~(0x1 << 4); |
| } |
| |
| |
| /* FSRT */ |
| psp_ctrl = psp_ctrl | (0x1 << 3); |
| |
| /* |
| *Three:adjust polarity between codec bit[3:0] of register[0x32] in acm_pm805.h and ssp here |
| *Bitclk Polarity bit[1:0] |
| */ |
| /* SCMODE */ |
| #if 0 |
| //psp_ctrl = psp_ctrl | (0x0 << 0); |
| psp_ctrl = psp_ctrl | (0x1 << 0); |
| //psp_ctrl = psp_ctrl | (0x2 << 0); |
| //psp_ctrl = psp_ctrl | (0x3 << 0); |
| #else |
| //for debug |
| if (0x3 == scmode) { |
| psp_ctrl = psp_ctrl | (0x3 << 0); |
| } else if (0x2 == scmode) { |
| psp_ctrl = psp_ctrl | (0x2 << 0); |
| } else if (0x1 == scmode) { |
| psp_ctrl = psp_ctrl | (0x1 << 0); |
| } else if (0x0 == scmode) { |
| psp_ctrl = psp_ctrl | (0x0 << 0); |
| } |
| #endif |
| |
| /* PSP_CTRL[0x18] */ |
| if (status_debug_tool) { |
| __raw_writel(get_reg_value_from_UI(PSP_CTRL), ssp->mmio_base + PSP_CTRL); |
| } else { |
| __raw_writel(psp_ctrl, ssp->mmio_base + PSP_CTRL); |
| } |
| |
| if ((32 == frame_bits) || (16 == frame_bits)) { |
| /* NETWORK_CTRL[0x1C] */ |
| /* FRDC: must be needed when slave */ |
| |
| /* Disable the network mode */ |
| if (status_debug_tool) { |
| __raw_writel(get_reg_value_from_UI(NET_WORK_CTRL), ssp->mmio_base + NET_WORK_CTRL); |
| } else { |
| __raw_writel(0, ssp->mmio_base + NET_WORK_CTRL); |
| } |
| } else if (64 == frame_bits) { |
| /* Enable the network mode */ |
| network_ctrl = network_ctrl | (0x3<<12);/* RX slot time */ |
| network_ctrl = network_ctrl | (0x3<<4);/* TX slot time */ |
| network_ctrl = network_ctrl | (0x1<<1);/* FRDC */ |
| network_ctrl = network_ctrl | (0x1<<0);/* NET_WORK_MOD */ |
| |
| if (status_debug_tool) { |
| __raw_writel(get_reg_value_from_UI(NET_WORK_CTRL), ssp->mmio_base + NET_WORK_CTRL); |
| } else { |
| __raw_writel(network_ctrl, ssp->mmio_base + NET_WORK_CTRL); |
| } |
| } |
| |
| //rwot_ctrl = rwot_ctrl | (1 << 0); |
| //__raw_writel(rwot_ctrl, ssp->mmio_base + RWOT_CTRL); |
| |
| #endif |
| } |
| |
| } |
| |
| //ssp_dump_registers(g_ssp); |
| |
| return 0; |
| } |
| |
| static int pxa_sndcard_lofi_startup(struct snd_pcm_substream *substream) |
| { |
| struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| struct snd_soc_dai *cpu_dai = rtd->cpu_dai; |
| struct snd_soc_dai *codec_dai = rtd->codec_dai; |
| |
| /* support narrow band: 8K */ |
| cpu_dai->driver->playback.rates = SNDRV_PCM_RATE_8000; |
| cpu_dai->driver->capture.rates = SNDRV_PCM_RATE_8000; |
| codec_dai->driver->playback.channels_max = 1; |
| codec_dai->driver->capture.channels_max = 1; |
| |
| return 0; |
| } |
| |
| static int pxa_sndcard_dummy_nb_startup(struct snd_pcm_substream *substream) |
| { |
| struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| struct snd_soc_dai *cpu_dai = rtd->cpu_dai; |
| struct snd_soc_dai *codec_dai = rtd->codec_dai; |
| |
| /* support both 8K*/ |
| cpu_dai->driver->playback.rates = SNDRV_PCM_RATE_8000; |
| cpu_dai->driver->capture.rates = SNDRV_PCM_RATE_8000; |
| codec_dai->driver->playback.channels_max = 1; |
| codec_dai->driver->capture.channels_max = 1; |
| codec_dai->driver->playback.formats = SNDRV_PCM_FMTBIT_S32_LE; |
| codec_dai->driver->capture.formats = SNDRV_PCM_FMTBIT_S32_LE; |
| |
| return 0; |
| } |
| |
| static int pxa_sndcard_dummy_wb_startup(struct snd_pcm_substream *substream) |
| { |
| struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| struct snd_soc_dai *cpu_dai = rtd->cpu_dai; |
| struct snd_soc_dai *codec_dai = rtd->codec_dai; |
| |
| /* support both 16K*/ |
| cpu_dai->driver->playback.rates = SNDRV_PCM_RATE_16000; |
| cpu_dai->driver->capture.rates = SNDRV_PCM_RATE_16000; |
| codec_dai->driver->playback.channels_max = 1; |
| codec_dai->driver->capture.channels_max = 1; |
| codec_dai->driver->playback.formats = SNDRV_PCM_FMTBIT_S32_LE; |
| codec_dai->driver->capture.formats = SNDRV_PCM_FMTBIT_S32_LE; |
| |
| return 0; |
| } |
| |
| static int pxa_sndcard_pcm_hifi_startup(struct snd_pcm_substream *substream) |
| { |
| struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| struct snd_soc_dai *cpu_dai = rtd->cpu_dai; |
| struct snd_soc_dai *codec_dai = rtd->codec_dai; |
| |
| /* support gssp hifi record: 44.1k */ |
| cpu_dai->driver->playback.rates = SNDRV_PCM_RATE_44100; |
| cpu_dai->driver->capture.rates = SNDRV_PCM_RATE_44100; |
| codec_dai->driver->playback.channels_max = 2; |
| codec_dai->driver->capture.channels_max = 2; |
| codec_dai->driver->playback.formats = SNDRV_PCM_FMTBIT_S16_LE; |
| codec_dai->driver->capture.formats = SNDRV_PCM_FMTBIT_S16_LE; |
| |
| return 0; |
| } |
| |
| static int pxa_sndcard_lofi_hw_params(struct snd_pcm_substream *substream, |
| struct snd_pcm_hw_params *params) |
| { |
| /* |
| * Since gssp is used for hifi record on HelanLTE A0, |
| * enable gssp when startup to eliminate noise. |
| * So move re-init gssp registers to pxa-ssp.c. |
| * Do nothing here. |
| */ |
| return 0; |
| } |
| |
| static int pxa_sndcard_lofi_prepare(struct snd_pcm_substream *substream) |
| { |
| return 0; |
| } |
| |
| //static struct snd_soc_jack hs_jack, hk_jack; |
| |
| static int pxa_sndcard_late_probe(struct snd_soc_card *card) |
| { |
| return 0; |
| } |
| |
| static struct snd_soc_ops pxa_sndcard_machine_ops[] = { |
| { |
| .startup = pxa_sndcard_hifi_startup, |
| .hw_params = pxa_sndcard_hw_params, |
| .prepare = pxa_sndcard_hifi_prepare, |
| }, |
| { |
| .startup = pxa_sndcard_lofi_startup, |
| .hw_params = pxa_sndcard_lofi_hw_params, |
| .prepare = pxa_sndcard_lofi_prepare, |
| }, |
| { |
| .startup = pxa_sndcard_dummy_nb_startup, |
| .hw_params = pxa_sndcard_lofi_hw_params, |
| .prepare = pxa_sndcard_lofi_prepare, |
| }, |
| { |
| .startup = pxa_sndcard_dummy_wb_startup, |
| .hw_params = pxa_sndcard_lofi_hw_params, |
| .prepare = pxa_sndcard_lofi_prepare, |
| }, |
| { |
| .startup = pxa_sndcard_pcm_hifi_startup, |
| .hw_params = pxa_sndcard_lofi_hw_params, |
| .prepare = pxa_sndcard_lofi_prepare, |
| }, |
| }; |
| |
| #if defined(CONFIG_CODEC_ALC5616) |
| |
| SND_SOC_DAILINK_DEFS(alc5616, |
| DAILINK_COMP_ARRAY(COMP_CPU("pxa-ssp-dai.1")), |
| DAILINK_COMP_ARRAY(COMP_CODEC("alc5616.0-001b", "alc5616-pcm")), |
| DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-ssp-dai.1"))); |
| |
| static struct snd_soc_dai_link pxa_alc5616_dai[] = { |
| { |
| .name = "alc5616 pcm", |
| .stream_name = "mrvl-ssp", |
| .ops = &pxa_sndcard_machine_ops[0], |
| SND_SOC_DAILINK_REG(alc5616), |
| }, |
| }; |
| |
| static struct snd_soc_card pxa_alc5616_card = { |
| .name = "pxa-alc5616-dkb-voice", |
| .dai_link = pxa_alc5616_dai, |
| .num_links = ARRAY_SIZE(pxa_alc5616_dai), |
| .late_probe = pxa_sndcard_late_probe, |
| }; |
| |
| #else |
| |
| SND_SOC_DAILINK_DEFS(sspbt, |
| DAILINK_COMP_ARRAY(COMP_CPU("pxa-ssp-dai.1")), |
| DAILINK_COMP_ARRAY(COMP_CODEC("sspbt.0-001c", "sspbt-pcm")), |
| DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-ssp-dai.1"))); |
| |
| static struct snd_soc_dai_link pxa_sspbt_dai[] = { |
| { |
| .name = "sspbt pcm", |
| .stream_name = "mrvl-ssp", |
| .ops = &pxa_sndcard_machine_ops[0], |
| SND_SOC_DAILINK_REG(sspbt), |
| }, |
| }; |
| |
| static struct snd_soc_card pxa_sspbt_card = { |
| .name = "pxa-sspbt-dkb-voice", |
| .dai_link = pxa_sspbt_dai, |
| .num_links = ARRAY_SIZE(pxa_sspbt_dai), |
| .late_probe = pxa_sndcard_late_probe, |
| }; |
| #endif |
| |
| #ifdef CONFIG_PM |
| static int pxa_sndcard_suspend(struct device *dev) |
| { |
| return 0; |
| } |
| |
| static int pxa_sndcard_resume(struct device *dev) |
| { |
| return 0; |
| } |
| #endif |
| |
| static SIMPLE_DEV_PM_OPS(pxa_sndcard_pm_ops, pxa_sndcard_suspend, |
| pxa_sndcard_resume); |
| |
| static struct of_device_id pxa_sndcard_dt_ids[] = { |
| { .compatible = "ASRMICRO,asrmicro-snd-card", }, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(of, pxa_sndcard_dt_ids); |
| |
| static int pxa_sndcard_probe(struct platform_device *pdev) |
| { |
| #if defined(CONFIG_CODEC_ALC5616) |
| struct snd_soc_card *card = &pxa_alc5616_card; |
| #else |
| struct snd_soc_card *card = &pxa_sspbt_card; |
| #endif |
| |
| int ret; |
| |
| card->dev = &pdev->dev; |
| |
| ret = devm_snd_soc_register_card(&pdev->dev, card); |
| if (ret) |
| dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", |
| ret); |
| |
| /* add ssp_master sysfs entries */ |
| ret = device_create_file(&pdev->dev, &dev_attr_ssp_master); |
| if (ret < 0) { |
| dev_err(&pdev->dev, |
| "%s: failed to add ssp_master sysfs files: %d\n", |
| __func__, ret); |
| goto err; |
| } |
| |
| /* add gssp_master sysfs entries */ |
| ret = device_create_file(&pdev->dev, &dev_attr_gssp_master); |
| if (ret < 0) { |
| dev_err(&pdev->dev, |
| "%s: failed to add gssp_master sysfs files: %d\n", |
| __func__, ret); |
| goto err; |
| } |
| |
| /* add gssp_record sysfs entries */ |
| ret = device_create_file(&pdev->dev, &dev_attr_gssp_record); |
| if (ret < 0) { |
| dev_err(&pdev->dev, |
| "%s: failed to add gssp_record sysfs files: %d\n", |
| __func__, ret); |
| goto err; |
| } |
| #ifdef CONFIG_SND_PXA_SSP_DUMP |
| /* add ssp_playback_dump sysfs entries */ |
| ret = device_create_file(&pdev->dev, &dev_attr_ssp_playback_dump.attr); |
| if (ret < 0) |
| dev_err(&pdev->dev, |
| "%s: failed to add ssp_playback_dump sysfs files: %d\n", |
| __func__, ret); |
| /* add ssp_capture_dump sysfs entries */ |
| ret = device_create_file(&pdev->dev, &dev_attr_ssp_capture_dump.attr); |
| if (ret < 0) |
| dev_err(&pdev->dev, |
| "%s: failed to add ssp_capture_dump sysfs files: %d\n", |
| __func__, ret); |
| /* add gssp_playback_dump sysfs entries */ |
| ret = device_create_file(&pdev->dev, &dev_attr_gssp_playback_dump.attr); |
| if (ret < 0) |
| dev_err(&pdev->dev, |
| "%s: failed to add gssp_playback_dump sysfs files: %d\n", |
| __func__, ret); |
| /* add gssp_capture_dump sysfs entries */ |
| ret = device_create_file(&pdev->dev, &dev_attr_gssp_capture_dump.attr); |
| if (ret < 0) |
| dev_err(&pdev->dev, |
| "%s: failed to add gssp_capture_dump sysfs files: %d\n", |
| __func__, ret); |
| #endif |
| return ret; |
| err: |
| snd_soc_unregister_card(card); |
| return ret; |
| } |
| |
| static int pxa_sndcard_remove(struct platform_device *pdev) |
| { |
| struct snd_soc_card *card = platform_get_drvdata(pdev); |
| |
| device_remove_file(&pdev->dev, &dev_attr_ssp_master); |
| device_remove_file(&pdev->dev, &dev_attr_gssp_master); |
| #ifdef CONFIG_SND_PXA_SSP_DUMP |
| device_remove_file(&pdev->dev, &dev_attr_ssp_playback_dump.attr); |
| device_remove_file(&pdev->dev, &dev_attr_ssp_capture_dump.attr); |
| device_remove_file(&pdev->dev, &dev_attr_gssp_playback_dump.attr); |
| device_remove_file(&pdev->dev, &dev_attr_gssp_capture_dump.attr); |
| #endif |
| snd_soc_unregister_card(card); |
| |
| return 0; |
| } |
| |
| static struct platform_driver pxa_sndcard_driver = { |
| .driver = { |
| .name = "pxa-sndcard-hifi", |
| .owner = THIS_MODULE, |
| .of_match_table = pxa_sndcard_dt_ids, |
| .pm = &pxa_sndcard_pm_ops, |
| }, |
| .probe = pxa_sndcard_probe, |
| .remove = pxa_sndcard_remove, |
| }; |
| |
| module_platform_driver(pxa_sndcard_driver); |
| |
| /* Module information */ |
| MODULE_AUTHOR("wenchen@asrmicro.com"); |
| MODULE_DESCRIPTION("ALSA SoC TTC DKB"); |
| MODULE_LICENSE("GPL"); |
| MODULE_ALIAS("platform:pxa-sndcard-hifi"); |