blob: 9a3d2eb3c6ce13aef5b48fa3d10c820d7612a242 [file] [log] [blame]
/*
* 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");