blob: 5d4e94247e23ec78e4b668a26c260f17470b2ca2 [file] [log] [blame]
#include "aai.h"
#include "aai_hw.h"
#include "aai_ioctl.h"
#include "aai_group.h"
/**
* Get a data from user space
*
* @param src ptr to userspace data
* @param dst ptr to kernelspace data
* @param size of data
* @return error code
*/
static int get_data(void __user *src, void *dst, int size)
{
int err = 0;
if (copy_from_user(dst, src, size))
err = -EFAULT;
return err;
}
/**
* Put data to userspace
*
* @param src ptr to userspace data
* @param dst ptr to kernelspace data
* @param size of data
*
* @return error code
*/
#if 0
static int put_data(void *src, void __user *dst, int size)
{
int err = 0;
if (copy_to_user(dst, src, size))
err = -EFAULT;
return err;
}
#endif
static int aai_hwdep_set_music_conf(struct card_data_t *aai,
unsigned long arg,
int musx)
{
int err = 0;
struct aai_ioctl_music_conf_t music_conf;
err = get_data((void __user *)arg, &music_conf,
sizeof(struct aai_ioctl_music_conf_t));
if (!err) {
if (0 <= music_conf.cycles_num && music_conf.cycles_num <= 127)
aai_set_nbit(aai, AAI_AAICFG_MUS(musx),
AAI_MUSx_CYCLES_NUM_MSK,
AAI_MUSx_CYCLES_NUM_SHIFT,
music_conf.cycles_num);
aai_setbit(aai, AAI_AAICFG_MUS(musx),
AAI_MUSx_MATSUSHITA_MSK, music_conf.matsushita);
aai_setbit(aai, AAI_AAICFG_MUS(musx),
AAI_MUSx_LEFT_FRAME_MSK, music_conf.left_frame);
aai_setbit(aai, AAI_AAICFG_MUS(musx),
AAI_MUSx_LSB_FIRST_MSK, music_conf.lsb_first);
aai_setbit(aai, AAI_AAICFG_MUS(musx),
AAI_MUSx_RIGHT_JUST_MSK, music_conf.right_just);
aai_setbit(aai, AAI_AAICFG_MUS(musx),
AAI_MUSx_24BITS_MSK, music_conf.bits24);
}
return err;
}
enum tdm_dir_t {
TDM_IN,
TDM_OUT
};
static int aai_hwdep_set_tdm_conf(struct card_data_t *aai,
unsigned long arg,
enum tdm_dir_t dir)
{
int err = 0;
struct aai_ioctl_tdm_conf_t tdm_conf;
uint32_t offset;
uint32_t cycle_mask;
uint32_t cycle_shift;
uint32_t lf_mask;
if (dir == TDM_IN) {
offset = AAI_AAICFG_TDM_IN;
cycle_mask = AAI_TDM_IN_CYCLES_NUM_MSK;
cycle_shift = AAI_TDM_IN_CYCLES_NUM_SHIFT;
lf_mask = AAI_TDM_IN_LEFT_FRAME_MSK;
} else {
offset = AAI_AAICFG_TDM_OUT;
cycle_mask = AAI_TDM_OUT_CYCLES_NUM_MSK;
cycle_shift = AAI_TDM_OUT_CYCLES_NUM_SHIFT;
lf_mask = AAI_TDM_OUT_LEFT_FRAME_MSK;
}
err = get_data((void __user *)arg, &tdm_conf,
sizeof(struct aai_ioctl_tdm_conf_t));
if (!err) {
if (8 <= tdm_conf.cycles_num && tdm_conf.cycles_num <= 127)
aai_set_nbit(aai, offset, cycle_mask,
cycle_shift, tdm_conf.cycles_num);
aai_setbit(aai, offset, lf_mask, tdm_conf.left_frame);
}
return err;
}
enum adc_type_t {
AAI_ADC,
AAI_DAC,
AAI_AUX
};
static int aai_hwdep_set_adc_conf(struct card_data_t *aai,
unsigned long arg,
enum adc_type_t type)
{
int err = 0;
struct aai_ioctl_adc_conf_t adc_conf;
switch (type) {
case AAI_ADC:
err = get_data((void __user *)arg, &adc_conf,
sizeof(struct aai_ioctl_adc_conf_t));
if (!err) {
if (0 <= adc_conf.cycles_num &&
adc_conf.cycles_num <= 127)
aai_set_nbit(aai, AAI_AAICFG_ADC,
AAI_ADC_CYCLES_NUM_MSK,
AAI_ADC_CYCLES_NUM_SHIFT,
adc_conf.cycles_num);
aai_setbit(aai, AAI_AAICFG_ADC,
AAI_ADC_MATSUSHITA_MSK, adc_conf.matsushita);
aai_setbit(aai, AAI_AAICFG_ADC,
AAI_ADC_LEFT_FRAME_MSK, adc_conf.left_frame);
}
break;
case AAI_DAC:
err = get_data((void __user *)arg, &adc_conf,
sizeof(struct aai_ioctl_adc_conf_t));
if (!err) {
if (0 <= adc_conf.cycles_num &&
adc_conf.cycles_num <= 127)
aai_set_nbit(aai, AAI_AAICFG_DAC,
AAI_DAC_CYCLES_NUM_MSK,
AAI_DAC_CYCLES_NUM_SHIFT,
adc_conf.cycles_num);
aai_setbit(aai, AAI_AAICFG_DAC,
AAI_DAC_MATSUSHITA_MSK, adc_conf.matsushita);
aai_setbit(aai, AAI_AAICFG_DAC,
AAI_DAC_LEFT_FRAME_MSK, adc_conf.left_frame);
}
break;
case AAI_AUX:
err = get_data((void __user *)arg, &adc_conf,
sizeof(struct aai_ioctl_adc_conf_t));
if (!err) {
if (0 <= adc_conf.cycles_num &&
adc_conf.cycles_num <= 127)
aai_set_nbit(aai, AAI_AAICFG_AUX,
AAI_AUX_CYCLES_NUM_MSK,
AAI_AUX_CYCLES_NUM_SHIFT,
adc_conf.cycles_num);
aai_setbit(aai, AAI_AAICFG_AUX,
AAI_AUX_HALF_FRAME_MSK, adc_conf.half_frame);
}
break;
default:
err = -EINVAL;
break;
}
return err;
}
static int aai_hwdep_set_pcm_conf(struct card_data_t *aai,
unsigned long arg,
int nb)
{
int err = 0;
struct aai_ioctl_pcm_conf_t pcm_conf;
err = get_data((void __user *)arg, &pcm_conf,
sizeof(struct aai_ioctl_pcm_conf_t));
if (!err) {
if (0 <= pcm_conf.start_two && pcm_conf.start_two <= 0xff)
aai_set_nbit(aai, AAI_AAICFG_PCM(nb),
AAI_PCM_START_TWO_MSK,
AAI_PCM_START_TWO_SHIFT,
pcm_conf.start_two);
if (0 <= pcm_conf.cycles_pcm_high &&
pcm_conf.cycles_pcm_high <= 0xff)
aai_set_nbit(aai, AAI_AAICFG_PCM(nb),
AAI_PCM_CYCLES_HIGH_MSK,
AAI_PCM_CYCLES_HIGH_SHIFT,
pcm_conf.cycles_pcm_high);
if (0 <= pcm_conf.cycles_pcm_low &&
pcm_conf.cycles_pcm_low <= 0x1ff)
aai_set_nbit(aai, AAI_AAICFG_PCM(nb),
AAI_PCM_CYCLES_LOW_MSK,
AAI_PCM_CYCLES_LOW_SHIFT,
pcm_conf.cycles_pcm_low);
aai_setbit(aai, AAI_AAICFG_PCM(nb),
AAI_PCM_OKI_MSK, pcm_conf.oki);
aai_setbit(aai, AAI_AAICFG_PCM(nb),
AAI_PCM_RUN_DUAL_MSK, pcm_conf.run_dual);
}
return err;
}
static int aai_hwdep_group_start(struct card_data_t *aai, unsigned long arg)
{
int err;
int group_nb;
err = get_data((void __user *)arg, &group_nb, sizeof(int));
if (!err)
err = aai_group_start(aai, group_nb);
return err;
}
static int aai_hwdep_group_sync(struct card_data_t *aai, unsigned long arg)
{
int err;
struct aai_ioctl_group_t group;
err = get_data((void __user *)arg, &group,
sizeof(struct aai_ioctl_group_t));
if (!err)
err = aai_group_set(aai, group.name, group.group);
return err;
}
static int aai_hwdep_set_mframe_asym_conf(struct card_data_t *aai,
unsigned long arg)
{
int err = 0;
struct aai_ioctl_asym_conf_t conf;
err = get_data((void __user *)arg, &conf,
sizeof(struct aai_ioctl_asym_conf_t));
aai_setbit(aai, AAI_LBC_CLKEN, AAI_LBC_CKEN_MFRAME_MSK, conf.enable);
aai_setbit(aai, AAI_LBC_MFRAME,
AAI_LBC_MFRAME_MFRAME_DIV_LOW_MSK, conf.div_low);
aai_setbit(aai, AAI_LBC_MFRAME,
AAI_LBC_MFRAME_MFRAME_DIV_HIGH_MSK, conf.div_high);
return err;
}
static int aai_hwdep_set_hframe_asym_conf(struct card_data_t *aai,
unsigned long arg)
{
int err = 0;
struct aai_ioctl_asym_conf_t conf;
err = get_data((void __user *)arg, &conf,
sizeof(struct aai_ioctl_asym_conf_t));
aai_setbit(aai, AAI_LBC_CLKEN, AAI_LBC_CKEN_HFRAME_MSK, conf.enable);
aai_setbit(aai, AAI_LBC_HFRAME,
AAI_LBC_HFRAME_HFRAME_DIV_LOW_MSK, conf.div_low);
aai_setbit(aai, AAI_LBC_HFRAME,
AAI_LBC_HFRAME_HFRAME_DIV_HIGH_MSK, conf.div_high);
return err;
}
/**
* AAI Driver IO control function
*/
static int aai_hwdep_ioctl(struct snd_hwdep *hwdep,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
int err = -EINVAL;
uint32_t val = 0;
struct card_data_t *aai = hwdep->private_data;
unsigned long flags = 0;
spin_lock_irqsave(&aai->hwlock, flags);
/**
* <ul>
*/
switch (cmd) {
/**
* <li><b>AAI_MUS0_CONF:</b><br>
* Set AAI music 0 configuration register<br>
* Param : AAI music configuration structure<br>
* Return : error code
* <hr>
*/
case AAI_MUS0_CONF:
err = aai_hwdep_set_music_conf(aai, arg, 0);
break;
/**
* <li><b>AAI_MUS1_CONF:</b><br>
* Set AAI music 1 configuration register<br>
* Param : AAI music configuration structure<br>
* Return : error code
* <hr>
*/
case AAI_MUS1_CONF:
err = aai_hwdep_set_music_conf(aai, arg, 1);
break;
/**
* <li><b>AAI_MUS2_CONF:</b><br>
* Set AAI music 2 configuration register<br>
* Param : AAI music configuration structure<br>
* Return : error code
* <hr>
*/
case AAI_MUS2_CONF:
err = aai_hwdep_set_music_conf(aai, arg, 2);
break;
/**
* <li><b>AAI_MUS3_CONF:</b><br>
* Set AAI music 3 configuration register<br>
* Param : AAI music configuration structure<br>
* Return : error code
* <hr>
*/
case AAI_MUS3_CONF:
err = aai_hwdep_set_music_conf(aai, arg, 3);
break;
/**
* <li><b>AAI_TDMIN_CONF:</b><br>
* Set AAI TDM input configuration register<br>
* Param : AAI TDM configuration structure<br>
* Return : error code
* <hr>
*/
case AAI_TDMIN_CONF:
err = aai_hwdep_set_tdm_conf(aai, arg, TDM_IN);
break;
/**
* <li><b>AAI_TDMOUT_CONF:</b><br>
* Set AAI TDM output configuration register<br>
* Param : AAI TDM configuration structure<br>
* Return : error code
* <hr>
*/
case AAI_TDMOUT_CONF:
err = aai_hwdep_set_tdm_conf(aai, arg, TDM_OUT);
break;
/**
* <li><b>AAI_ADC_CONF:</b><br>
* Set AAI ADC configuration register<br>
* Param : AAI ADC configuration structure<br>
* Return : error code
* <hr>
*/
case AAI_ADC_CONF:
err = aai_hwdep_set_adc_conf(aai, arg, AAI_ADC);
break;
/**
* <li><b>AAI_DAC_CONF:</b><br>
* Set AAI DAC configuration register<br>
* Param : AAI ADC configuration structure<br>
* Return : error code
* <hr>
*/
case AAI_DAC_CONF:
err = aai_hwdep_set_adc_conf(aai, arg, AAI_DAC);
break;
/**
* <li><b>AAI_AUX_CONF:</b><br>
* Set AAI AUX configuration register<br>
* Param : AAI ADC configuration structure<br>
* Return : error code
* <hr>
*/
case AAI_AUX_CONF:
err = aai_hwdep_set_adc_conf(aai, arg, AAI_AUX);
break;
/**
* <li><b>AAI_PCM1_CONF:</b><br>
* Set AAI PCM 1 configuration register<br>
* Param : AAI PCM configuration structure<br>
* Return : error code
* <hr>
*/
case AAI_PCM1_CONF:
err = aai_hwdep_set_pcm_conf(aai, arg, 0);
break;
/**
* <li><b>AAI_PCM2_CONF:</b><br>
* Set AAI PCM 2 configuration register<br>
* Param : AAI PCM configuration structure<br>
* Return : error code
* <hr>
*/
case AAI_PCM2_CONF:
err = aai_hwdep_set_pcm_conf(aai, arg, 1);
break;
/**
* <li><b>AAI_VOICE_SPEED:</b><br>
* Set AAI VOICE_SPEED register<br>
* Param : requested voice speed, recommended: 0x810E35 for 16k/8k;
0xB1DAC7 for 22.05k/11k<br>
* Return : error code
* <hr>
*/
case AAI_VOICE_SPEED:
err = get_data((void __user *)arg, &val, sizeof(uint32_t));
if (!err) {
if (0 <= val && val <= 0xffffff)
aai_set_nbit(aai, AAI_AAICFG_VOICE_SPEED,
0xffffff, 0, val);
}
break;
/**
* <li><b>AAI_VOICE_8K_SPEAKER:</b><br>
* Set AAI VOICE_8K register for speaker<br>
* Param : Set 0 for 16k speaker, 1 for 8k speaker<br>
* Return : error code
* <hr>
*/
case AAI_VOICE_8K_SPEAKER:
err = get_data((void __user *)arg, &val, sizeof(uint32_t));
if (!err)
aai_setbit(aai, AAI_AAICFG_VOICE_8K,
AAI_VOICE_8K_SPEAKER_MSK, !!val);
break;
/**
* <li><b>AAI_VOICE_8K_PHONE:</b><br>
* Set AAI VOICE_8K register for phone<br>
* Param : Set 0 for 16k phone, 1 for 8k phone<br>
* Return : error code
* <hr>
*/
case AAI_VOICE_8K_PHONE:
err = get_data((void __user *)arg, &val, sizeof(uint32_t));
if (!err)
aai_setbit(aai, AAI_AAICFG_VOICE_8K,
AAI_VOICE_8K_PHONE_MSK, !!val);
break;
/**
* <li><b>AAI_GROUP_START:</b><br>
* Start audio group specified in argument<br>
* Param : group to be started<br>
* Return : error code
* <hr>
*/
case AAI_GROUP_START:
err = aai_hwdep_group_start(aai, arg);
break;
/**
* <li><b>AAI_GROUP_SYNC:</b><br>
* Assign audio channel to a group<br>
* Param : structure containing channel name and group number<br>
* Return : error code
* <hr>
*/
case AAI_GROUP_SYNC:
err = aai_hwdep_group_sync(aai, arg);
break;
/**
* <li><b>AAI_I2S_ASYMMETRIC:</b><br>
* Set AAI I2S asymmetric configuration register<br>
* Param : AAI I2S asymmetric configuration structure<br>
* Return : error code
* <hr>
*/
case AAI_MFRAME_ASYMMETRIC_CONF:
err = aai_hwdep_set_mframe_asym_conf(aai, arg);
break;
/**
* <li><b>AAI_TDM_ASYMMETRIC:</b><br>
* Set AAI TDM asymmetric configuration register<br>
* Param : AAI TDM asymmetric configuration structure<br>
* Return : error code
* <hr>
*/
case AAI_HFRAME_ASYMMETRIC_CONF:
err = aai_hwdep_set_hframe_asym_conf(aai, arg);
break;
default:
break;
}
/**
* </ul>
*/
spin_unlock_irqrestore(&aai->hwlock, flags);
return err;
}
static int aai_hwdep_dummy(struct snd_hwdep *hw, struct file *file)
{
return 0;
}
int aai_ioctl_hwdep_new(struct card_data_t *aai)
{
struct snd_hwdep *rhwdep;
int err = 0;
err = snd_hwdep_new(aai->card, "AAIhwdep", 0, &rhwdep);
if (err < 0)
return err;
rhwdep->private_data = aai;
rhwdep->ops.open = aai_hwdep_dummy;
rhwdep->ops.ioctl = aai_hwdep_ioctl;
rhwdep->ops.release = aai_hwdep_dummy;
return 0;
}