/*
* Copyright (C) ST-Ericsson SA 2012
*
* Author: Ola Lilja <ola.o.lilja@stericsson.com>,
* Roger Nilsson <roger.xr.nilsson@stericsson.com>
* for ST-Ericsson.
*
* License terms:
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include <linux/mfd/dbx500-prcmu.h>
#include <mach/hardware.h>
#include <mach/msp.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include "ux500_msp_i2s.h"
#include "ux500_msp_dai.h"
#include "ux500_pcm.h"
static int setup_pcm_multichan(struct snd_soc_dai *dai,
struct ux500_msp_config *msp_config)
{
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
struct msp_multichannel_config *multi =
&msp_config->multichannel_config;
if (drvdata->slots > 1) {
msp_config->multichannel_configured = 1;
multi->tx_multichannel_enable = true;
multi->rx_multichannel_enable = true;
multi->rx_comparison_enable_mode = MSP_COMPARISON_DISABLED;
multi->tx_channel_0_enable = drvdata->tx_mask;
multi->tx_channel_1_enable = 0;
multi->tx_channel_2_enable = 0;
multi->tx_channel_3_enable = 0;
multi->rx_channel_0_enable = drvdata->rx_mask;
multi->rx_channel_1_enable = 0;
multi->rx_channel_2_enable = 0;
multi->rx_channel_3_enable = 0;
dev_dbg(dai->dev,
"%s: Multichannel enabled. Slots: %d, TX: %u, RX: %u\n",
__func__, drvdata->slots, multi->tx_channel_0_enable,
multi->rx_channel_0_enable);
}
return 0;
}
static int setup_frameper(struct snd_soc_dai *dai, unsigned int rate,
struct msp_protdesc *prot_desc)
{
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
switch (drvdata->slots) {
case 1:
switch (rate) {
case 8000:
prot_desc->frame_period =
FRAME_PER_SINGLE_SLOT_8_KHZ;
break;
case 16000:
prot_desc->frame_period =
FRAME_PER_SINGLE_SLOT_16_KHZ;
break;
case 44100:
prot_desc->frame_period =
FRAME_PER_SINGLE_SLOT_44_1_KHZ;
break;
case 48000:
prot_desc->frame_period =
FRAME_PER_SINGLE_SLOT_48_KHZ;
break;
default:
dev_err(dai->dev,
"%s: Error: Unsupported sample-rate (freq = %d)!\n",
__func__, rate);
return -EINVAL;
}
break;
case 2:
prot_desc->frame_period = FRAME_PER_2_SLOTS;
break;
case 8:
prot_desc->frame_period = FRAME_PER_8_SLOTS;
break;
case 16:
prot_desc->frame_period = FRAME_PER_16_SLOTS;
break;
default:
dev_err(dai->dev,
"%s: Error: Unsupported slot-count (slots = %d)!\n",
__func__, drvdata->slots);
return -EINVAL;
}
prot_desc->clocks_per_frame =
prot_desc->frame_period+1;
dev_dbg(dai->dev, "%s: Clocks per frame: %u\n",
__func__,
prot_desc->clocks_per_frame);
return 0;
}
static int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate,
struct msp_protdesc *prot_desc)
{
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
u32 frame_length = MSP_FRAME_LEN_1;
prot_desc->frame_width = 0;
switch (drvdata->slots) {
case 1:
frame_length = MSP_FRAME_LEN_1;
break;
case 2:
frame_length = MSP_FRAME_LEN_2;
break;
case 8:
frame_length = MSP_FRAME_LEN_8;
break;
case 16:
frame_length = MSP_FRAME_LEN_16;
break;
default:
dev_err(dai->dev,
"%s: Error: Unsupported slot-count (slots = %d)!\n",
__func__, drvdata->slots);
return -EINVAL;
}
prot_desc->tx_frame_len_1 = frame_length;
prot_desc->rx_frame_len_1 = frame_length;
prot_desc->tx_frame_len_2 = frame_length;
prot_desc->rx_frame_len_2 = frame_length;
prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16;
prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16;
prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16;
prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16;
return setup_frameper(dai, rate, prot_desc);
}
static int setup_clocking(struct snd_soc_dai *dai,
unsigned int fmt,
struct ux500_msp_config *msp_config)
{
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_NB_IF:
msp_config->tx_fsync_pol ^= 1 << TFSPOL_SHIFT;
msp_config->rx_fsync_pol ^= 1 << RFSPOL_SHIFT;
break;
default:
dev_err(dai->dev,
"%s: Error: Unsopported inversion (fmt = 0x%x)!\n",
__func__, fmt);
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM: