/*
* wm_hubs.c -- WM8993/4 common code
*
* Copyright 2009 Wolfson Microelectronics plc
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
*
* 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/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/mfd/wm8994/registers.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "wm8993.h"
#include "wm_hubs.h"
const DECLARE_TLV_DB_SCALE(wm_hubs_spkmix_tlv, -300, 300, 0);
EXPORT_SYMBOL_GPL(wm_hubs_spkmix_tlv);
static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1650, 150, 0);
static const DECLARE_TLV_DB_SCALE(inmix_sw_tlv, 0, 3000, 0);
static const DECLARE_TLV_DB_SCALE(inmix_tlv, -1500, 300, 1);
static const DECLARE_TLV_DB_SCALE(earpiece_tlv, -600, 600, 0);
static const DECLARE_TLV_DB_SCALE(outmix_tlv, -2100, 300, 0);
static const DECLARE_TLV_DB_SCALE(spkmixout_tlv, -1800, 600, 1);
static const DECLARE_TLV_DB_SCALE(outpga_tlv, -5700, 100, 0);
static const unsigned int spkboost_tlv[] = {
TLV_DB_RANGE_HEAD(2),
0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0),
};
static const DECLARE_TLV_DB_SCALE(line_tlv, -600, 600, 0);
static const char *speaker_ref_text[] = {
"SPKVDD/2",
"VMID",
};
static const struct soc_enum speaker_ref =
SOC_ENUM_SINGLE(WM8993_SPEAKER_MIXER, 8, 2, speaker_ref_text);
static const char *speaker_mode_text[] = {
"Class D",
"Class AB",
};
static const struct soc_enum speaker_mode =
SOC_ENUM_SINGLE(WM8993_SPKMIXR_ATTENUATION, 8, 2, speaker_mode_text);
static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op)
{
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
unsigned int reg;
int count = 0;
int timeout;
unsigned int val;
val = op | WM8993_DCS_ENA_CHAN_0 | WM8993_DCS_ENA_CHAN_1;
/* Trigger the command */
snd_soc_write(codec, WM8993_DC_SERVO_0, val);
dev_dbg(codec->dev, "Waiting for DC servo...\n");
if (hubs->dcs_done_irq)
timeout = 4;
else
timeout = 400;
do {
count++;
if (hubs->dcs_done_irq)
wait_for_completion_timeout(&hubs->dcs_done,
msecs_to_jiffies(250));
else
msleep(1);
reg = snd_soc_read(codec, WM8993_DC_SERVO_0);
dev_dbg(codec->dev, "DC servo: %x\n", reg);
} while (reg & op && count < timeout);
if (reg & op)
dev_err(codec->dev, "Timed out waiting for DC Servo %x\n",
op);
}
irqreturn_t wm_hubs_dcs_done(int irq, void *data)
{
struct wm_hubs_data *hubs = data;
complete(&hubs->dcs_done);
return IRQ_HANDLED;
}
EXPORT_SYMBOL_GPL(wm_hubs_dcs_done);
/*
* Startup calibration of the DC servo
*/
static void calibrate_dc_servo(struct snd_soc_codec *codec)
{
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
s8 offset;
u16 reg, reg_l, reg_r, dcs_cfg, dcs_reg;
switch (hubs->dcs_readback_mode) {
case 2:
dcs_reg =