/*
* alc5623.c -- alc562[123] ALSA Soc Audio driver
*
* Copyright 2008 Realtek Microelectronics
* Author: flove <flove@realtek.com> Ethan <eku@marvell.com>
*
* Copyright 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
*
*
* Based on WM8753.c
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/alc5623.h>
#include "alc5623.h"
static int caps_charge = 2000;
module_param(caps_charge, int, 0);
MODULE_PARM_DESC(caps_charge, "ALC5623 cap charge time (msecs)");
/* codec private data */
struct alc5623_priv {
enum snd_soc_control_type control_type;
u8 id;
unsigned int sysclk;
u16 reg_cache[ALC5623_VENDOR_ID2+2];
unsigned int add_ctrl;
unsigned int jack_det_ctrl;
};
static void alc5623_fill_cache(struct snd_soc_codec *codec)
{
int i, step = codec->driver->reg_cache_step;
u16 *cache = codec->reg_cache;
/* not really efficient ... */
codec->cache_bypass = 1;
for (i = 0 ; i < codec->driver->reg_cache_size ; i += step)
cache[i] = snd_soc_read(codec, i);
codec->cache_bypass = 0;
}
static inline int alc5623_reset(struct snd_soc_codec *codec)
{
return snd_soc_write(codec, ALC5623_RESET, 0);
}
static int amp_mixer_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
/* to power-on/off class-d amp generators/speaker */
/* need to write to 'index-46h' register : */
/* so write index num (here 0x46) to reg 0x6a */
/* and then 0xffff/0 to reg 0x6c */
snd_soc_write(w->codec, ALC5623_HID_CTRL_INDEX, 0x46);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
snd_soc_write(w->codec, ALC5623_HID_CTRL_DATA, 0xFFFF);
break;
case SND_SOC_DAPM_POST_PMD:
snd_soc_write(w->codec, ALC5623_HID_CTRL_DATA, 0);
break;
}
return 0;
}
/*
* ALC5623 Controls
*/
static const DECLARE_TLV_DB_SCALE(vol_tlv, -3450, 150, 0);
static const DECLARE_TLV_DB_SCALE(hp_tlv, -4650, 150, 0);
static const DECLARE_TLV_DB_SCALE(adc_rec_tlv, -1650, 150, 0);
static const unsigned int boost_tlv[] = {
TLV_DB_RANGE_HEAD(3),
0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
};
static const DECLARE_TLV_DB_SCALE(dig_tlv, 0, 600, 0);
static const struct snd_kcontrol_new alc5621_vol_snd_controls[] = {
SOC_DOUBLE_TLV("Speaker Playback Volume",
ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv),
SOC_DOUBLE("Speaker Playback Switch",
ALC5623_SPK_OUT_VOL, 15, 7, 1, 1),
SOC_DOUBLE_TLV("Headphone Playback Volume",
ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv),
SOC_DOUBLE("Headphone Playback Switch",
ALC5623_HP_OUT_VOL, 15, 7, 1, 1),
};
static const struct snd_kcontrol_new alc5622_vol_snd_controls[] = {
SOC_DOUBLE_TLV("Speaker Playback Volume",
ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv),
SOC_DOUBLE("Speaker Playback Switch",
ALC5623_SPK_OUT_VOL, 15, 7, 1, 1),
SOC_DOUBLE_TLV("Line Playback Volume",
ALC5623_HP_OUT_VOL, 8, 0,