* GPIO 1 -> DFS1 of AK5385
*/
+#include <linux/mutex.h>
#include <linux/pci.h>
+#include <sound/ac97_codec.h>
#include <sound/control.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include "oxygen.h"
+#include "ak4396.h"
+#include "cm9780.h"
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("C-Media CMI8788 driver");
{ OXYGEN_PCI_SUBID(0x13f6, 0x0010) },
{ OXYGEN_PCI_SUBID(0x13f6, 0x8788) },
{ OXYGEN_PCI_SUBID(0x147a, 0xa017) },
- { OXYGEN_PCI_SUBID(0x14c3, 0x1710) },
- { OXYGEN_PCI_SUBID(0x14c3, 0x1711) },
{ OXYGEN_PCI_SUBID(0x1a58, 0x0910) },
{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = 1 },
{ OXYGEN_PCI_SUBID(0x7284, 0x9761) },
#define GPIO_AK5385_DFS_DOUBLE 0x0001
#define GPIO_AK5385_DFS_QUAD 0x0002
-#define AK4396_WRITE 0x2000
-
-#define AK4396_CONTROL_1 0
-#define AK4396_CONTROL_2 1
-#define AK4396_CONTROL_3 2
-#define AK4396_LCH_ATT 3
-#define AK4396_RCH_ATT 4
-
-/* control 1 */
-#define AK4396_RSTN 0x01
-#define AK4396_DIF_MASK 0x0e
-#define AK4396_DIF_16_LSB 0x00
-#define AK4396_DIF_20_LSB 0x02
-#define AK4396_DIF_24_MSB 0x04
-#define AK4396_DIF_24_I2S 0x06
-#define AK4396_DIF_24_LSB 0x08
-#define AK4396_ACKS 0x80
-/* control 2 */
-#define AK4396_SMUTE 0x01
-#define AK4396_DEM_MASK 0x06
-#define AK4396_DEM_441 0x00
-#define AK4396_DEM_OFF 0x02
-#define AK4396_DEM_48 0x04
-#define AK4396_DEM_32 0x06
-#define AK4396_DFS_MASK 0x18
-#define AK4396_DFS_NORMAL 0x00
-#define AK4396_DFS_DOUBLE 0x08
-#define AK4396_DFS_QUAD 0x10
-#define AK4396_SLOW 0x20
-#define AK4396_DZFM 0x40
-#define AK4396_DZFE 0x80
-/* control 3 */
-#define AK4396_DZFB 0x04
-#define AK4396_DCKB 0x10
-#define AK4396_DCKS 0x20
-#define AK4396_DSDM 0x40
-#define AK4396_D_P_MASK 0x80
-#define AK4396_PCM 0x00
-#define AK4396_DSD 0x80
+#define GPIO_LINE_MUTE CM9780_GPO0
#define WM8785_R0 0
#define WM8785_R1 1
#define WM8785_PWRDNL 0x010
#define WM8785_TDM_MASK 0x1c0
+struct generic_data {
+ u8 ak4396_ctl2;
+};
+
static void ak4396_write(struct oxygen *chip, unsigned int codec,
u8 reg, u8 value)
{
static void ak4396_init(struct oxygen *chip)
{
+ struct generic_data *data = chip->model_data;
unsigned int i;
- chip->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+ data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL;
for (i = 0; i < 4; ++i) {
ak4396_write(chip, i,
AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
ak4396_write(chip, i,
- AK4396_CONTROL_2, chip->ak4396_ctl2);
+ AK4396_CONTROL_2, data->ak4396_ctl2);
ak4396_write(chip, i,
AK4396_CONTROL_3, AK4396_PCM);
ak4396_write(chip, i, AK4396_LCH_ATT, 0xff);
snd_component_add(chip->card, "WM8785");
}
+static void cmi9780_init(struct oxygen *chip)
+{
+ oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, GPIO_LINE_MUTE);
+}
+
static void generic_init(struct oxygen *chip)
{
ak4396_init(chip);
wm8785_init(chip);
+ cmi9780_init(chip);
}
static void meridian_init(struct oxygen *chip)
{
ak4396_init(chip);
ak5385_init(chip);
+ cmi9780_init(chip);
}
static void generic_cleanup(struct oxygen *chip)
{
}
-static void generic_pcm_hardware_filter(unsigned int channel,
- struct snd_pcm_hardware *hardware)
-{
- if (channel == PCM_A) {
- hardware->rates = SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000;
- hardware->rate_min = 44100;
- }
-}
-
static void set_ak4396_params(struct oxygen *chip,
struct snd_pcm_hw_params *params)
{
+ struct generic_data *data = chip->model_data;
unsigned int i;
u8 value;
- value = chip->ak4396_ctl2 & ~AK4396_DFS_MASK;
+ value = data->ak4396_ctl2 & ~AK4396_DFS_MASK;
if (params_rate(params) <= 54000)
value |= AK4396_DFS_NORMAL;
- else if (params_rate(params) < 120000)
+ else if (params_rate(params) <= 108000)
value |= AK4396_DFS_DOUBLE;
else
value |= AK4396_DFS_QUAD;
- chip->ak4396_ctl2 = value;
+ data->ak4396_ctl2 = value;
for (i = 0; i < 4; ++i) {
ak4396_write(chip, i,
AK4396_CONTROL_1, AK4396_DIF_24_MSB);
static void update_ak4396_mute(struct oxygen *chip)
{
+ struct generic_data *data = chip->model_data;
unsigned int i;
u8 value;
- value = chip->ak4396_ctl2 & ~AK4396_SMUTE;
+ value = data->ak4396_ctl2 & ~AK4396_SMUTE;
if (chip->dac_mute)
value |= AK4396_SMUTE;
+ data->ak4396_ctl2 = value;
for (i = 0; i < 4; ++i)
ak4396_write(chip, i, AK4396_CONTROL_2, value);
}
wm8785_write(chip, WM8785_R7, 0);
value = WM8785_MCR_SLAVE | WM8785_FORMAT_LJUST;
- if (params_rate(params) == 96000)
+ if (params_rate(params) <= 48000)
+ value |= WM8785_OSR_SINGLE;
+ else if (params_rate(params) <= 96000)
value |= WM8785_OSR_DOUBLE;
- else if (params_rate(params) == 192000)
- value |= WM8785_OSR_QUAD;
else
- value |= WM8785_OSR_SINGLE;
+ value |= WM8785_OSR_QUAD;
wm8785_write(chip, WM8785_R0, value);
if (snd_pcm_format_width(params_format(params)) <= 16)
value, GPIO_AK5385_DFS_MASK);
}
+static void cmi9780_switch_hook(struct oxygen *chip, unsigned int codec,
+ unsigned int reg, int mute)
+{
+ if (codec != 0)
+ return;
+ switch (reg) {
+ case AC97_LINE:
+ oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS,
+ mute ? GPIO_LINE_MUTE : 0,
+ GPIO_LINE_MUTE);
+ break;
+ case AC97_MIC:
+ case AC97_CD:
+ case AC97_AUX:
+ if (!mute)
+ oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_STATUS,
+ GPIO_LINE_MUTE);
+ break;
+ }
+}
+
static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
static int ak4396_control_filter(struct snd_kcontrol_new *template)
.init = generic_init,
.control_filter = ak4396_control_filter,
.cleanup = generic_cleanup,
- .pcm_hardware_filter = generic_pcm_hardware_filter,
.set_dac_params = set_ak4396_params,
.set_adc_params = set_wm8785_params,
.update_dac_volume = update_ak4396_volume,
.update_dac_mute = update_ak4396_mute,
+ .ac97_switch_hook = cmi9780_switch_hook,
+ .model_data_size = sizeof(struct generic_data),
+ .dac_channels = 8,
.used_channels = OXYGEN_CHANNEL_A |
OXYGEN_CHANNEL_C |
OXYGEN_CHANNEL_SPDIF |
.set_adc_params = set_ak5385_params,
.update_dac_volume = update_ak4396_volume,
.update_dac_mute = update_ak4396_mute,
+ .ac97_switch_hook = cmi9780_switch_hook,
+ .model_data_size = sizeof(struct generic_data),
+ .dac_channels = 8,
.used_channels = OXYGEN_CHANNEL_B |
OXYGEN_CHANNEL_C |
OXYGEN_CHANNEL_SPDIF |
const struct pci_device_id *pci_id)
{
static int dev;
- const struct oxygen_model *model;
+ int is_meridian;
int err;
if (dev >= SNDRV_CARDS)
++dev;
return -ENOENT;
}
- model = pci_id->driver_data ? &model_meridian : &model_generic;
- err = oxygen_pci_probe(pci, index[dev], id[dev], 1, model);
+ is_meridian = pci_id->driver_data;
+ err = oxygen_pci_probe(pci, index[dev], id[dev], is_meridian,
+ is_meridian ? &model_meridian : &model_generic);
if (err >= 0)
++dev;
return err;