/* * Audio support for codec Philips UDA1380 * * 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. * * Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org> */ #include "adriver.h" #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> #include <linux/string.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/ioctl.h> #include <linux/delay.h> #include <linux/i2c.h> #include <sound/core.h> #include <sound/control.h> #include <sound/initval.h> #include <sound/info.h> #include <asm/byteorder.h> #include <sound/uda1380.h> /* begin {{ I2C }} */ static struct i2c_driver snd_uda1380_i2c_driver = { .driver = { .name = "uda1380-i2c" }, }; static int snd_uda1380_i2c_init(void) { return i2c_add_driver(&snd_uda1380_i2c_driver); } static void snd_uda1380_i2c_free(void) { i2c_del_driver(&snd_uda1380_i2c_driver); } static inline int snd_uda1380_i2c_probe(struct snd_uda1380 *uda) { if (uda->i2c_client.adapter == NULL || (uda->i2c_client.addr & 0xfd) != 0x18) return -EINVAL; if (i2c_smbus_xfer(uda->i2c_client.adapter, uda->i2c_client.addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL) < 0) return -ENODEV; else return 0; } static int snd_uda1380_i2c_attach(struct snd_uda1380 *uda) { int ret = 0; if ((ret = snd_uda1380_i2c_probe(uda)) < 0) return ret; snprintf(uda->i2c_client.name, sizeof(uda->i2c_client.name), "uda1380-i2c at %d-%04x", i2c_adapter_id(uda->i2c_client.adapter), uda->i2c_client.addr); return i2c_attach_client(&uda->i2c_client); } static void snd_uda1380_i2c_detach(struct snd_uda1380 *uda) { i2c_detach_client(&uda->i2c_client); } /* end {{ I2C }} */ /* begin {{ Registers Cache <--> HW }} */ static inline void _cpu_to_be16_string(u16 *to, u16 *from, unsigned int count) { #ifdef __BIG_ENDIAN memcpy(to, from, count << 1); #else u16 *i_to, *i_from; for (i_to = to, i_from = from; i_to < to + count; i_to++, i_from++) *i_to = cpu_to_be16(*i_from); #endif } static inline void _be16_to_cpu_string(u16 *to, u16 *from, unsigned int count) { #ifdef __BIG_ENDIAN memcpy(to, from, count << 1); #else u16 *i_to, *i_from; for (i_to = to, i_from = from; i_to < to + count; i_to++, i_from++) *i_to = be16_to_cpu(*i_from); #endif } /* transfer of up to 5 consecutive regs (a section)*/ static int snd_uda1380_hwsync(struct snd_uda1380 *uda, int read, u8 start_reg, unsigned int count) { struct i2c_msg msgs[2]; u8 buf[11]; int ret; if (snd_BUG_ON(count * 2 >= sizeof(buf))) return -EINVAL; if (snd_BUG_ON(start_reg >= ARRAY_SIZE(uda->regs))) return -EINVAL; if (snd_BUG_ON(start_reg + count > ARRAY_SIZE(uda->regs))) return -EINVAL; /* setup i2c msgs */ msgs[0].addr = uda->i2c_client.addr; msgs[0].flags = 0; msgs[0].buf = buf; if (!read) msgs[0].len = (count << 1) + 1; else { msgs[1].flags = I2C_M_RD; msgs[1].addr = msgs[0].addr; msgs[1].buf = msgs[0].buf + 1; msgs[0].len = 1; msgs[1].len = count << 1; } buf[0] = start_reg; /* regs -> buffer, on write */ if (!read && start_reg != 0x7f) /* 0x7f: software reset */ _cpu_to_be16_string((u16*) (buf + 1), &uda->regs[start_reg], count); /* i2c transfer */ ret = i2c_transfer(uda->i2c_client.adapter, msgs, read ? 2 : 1); if (ret != (read ? 2 : 1)) return ret; /* transfer error */ //@@ error ret < 0, or not ? /* regs <- buffer, on read */ if (read && start_reg != 0x7f) /* 0x7f: software reset */ _be16_to_cpu_string(&uda->regs[start_reg], (u16*) (buf + 1), count); return 0; } static inline int snd_uda1380_hwsync_read(struct snd_uda1380 *uda, u8 start_reg, unsigned int count) { return snd_uda1380_hwsync(uda, 1, start_reg, count); } static inline int snd_uda1380_hwsync_write(struct snd_uda1380 *uda, u8 start_reg, unsigned int count) { return snd_uda1380_hwsync(uda, 0, start_reg, count); } /* end {{ Registers Cache <--> HW }} */ /* begin {{ Registers Cache Ops }} */ static inline int _cache_flush1(struct snd_uda1380 *uda, unsigned int i) { snd_uda1380_hwsync_write(uda, uda->cache_dirty[i].start_reg, uda->cache_dirty[i].count); uda->cache_dirty[i].start_reg = i << 4; uda->cache_dirty[i].count = 0; return 0; } static inline int snd_uda1380_cache_try_flush(struct snd_uda1380 *uda) { if (uda->cache_dirty[0].count) if (uda->powered_on) _cache_flush1(uda, 0); if (uda->cache_dirty[1].count) if (uda->playback_clock_on) _cache_flush1(uda, 1); if (uda->cache_dirty[2].count) if (uda->capture_clock_on) _cache_flush1(uda, 2); return 0; } /* Note: regs in the same section */ static int snd_uda1380_cache_dirty_zone(struct snd_uda1380 *uda, u8 start_reg, unsigned int count) { unsigned int i = start_reg >> 4; if (!count) return 0; if (!uda->cache_dirty[i].count) { uda->cache_dirty[i].start_reg = start_reg; uda->cache_dirty[i].count = count; } else { int sr0 = uda->cache_dirty[i].start_reg; int er0 = sr0 + uda->cache_dirty[i].count -1; int sr1 = start_reg; int er1 = sr1 + count -1; int sr = (sr1 <= sr0) ? sr1 : sr0; int er = (er1 >= er0) ? er1 : er0; uda->cache_dirty[i].start_reg = (u8) sr; uda->cache_dirty[i].count = (u8) (er - sr + 1); } snd_uda1380_cache_try_flush(uda); return 0; } static inline int snd_uda1380_cache_dirty(struct snd_uda1380 *uda, u8 reg) { return snd_uda1380_cache_dirty_zone(uda, reg, 1); } static inline int snd_uda1380_cache_dirty_all(struct snd_uda1380 *uda) { snd_uda1380_cache_dirty_zone(uda, 0x00, 5); snd_uda1380_cache_dirty_zone(uda, 0x10, 5); snd_uda1380_cache_dirty_zone(uda, 0x20, 4); return 0; } /* end {{ Registers Cache Ops }} */ static inline void snd_uda1380_lock(struct snd_uda1380 *uda) { down(&uda->sem); } static inline void snd_uda1380_unlock(struct snd_uda1380 *uda) { up(&uda->sem); } #define WRITE_MASK(i, val, mask) (((i) & ~(mask)) | ((val) & (mask))) /* begin {{ Controls }} */ /* a control element in a register */ struct snd_uda1380_uctl_reg_elem_int { unsigned int is_stereo:1, inv_range:1, /* inverted range */ rot_range:1, /* rotated half range (from a 2's complement) */ reg:8, shift:4, /* shift */ mask:16; }; #define ROT_RANGE(val, mask) \ (((val) + (((mask) + 1) >> 1)) & (mask)) #define INV_RANGE(val, mask) \ (~(val) & (mask)) static int snd_uda1380_actl_reg_elem_int_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct snd_uda1380_uctl_reg_elem_int *uctl = (struct snd_uda1380_uctl_reg_elem_int *) kcontrol->private_value; uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = uctl->is_stereo ? 2 : 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = uctl->is_stereo ? uctl->mask & 0x00ff : uctl->mask; return 0; } static int snd_uda1380_actl_reg_elem_int_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_uda1380 *uda = (struct snd_uda1380 *) kcontrol->private_data; struct snd_uda1380_uctl_reg_elem_int *uctl = (struct snd_uda1380_uctl_reg_elem_int *) kcontrol->private_value; unsigned int val, val_l, val_r, mask1; val = uda->regs[uctl->reg] >> uctl->shift & uctl->mask; if (uctl->is_stereo) { val_l = val >> 8; val_r = val & 0x00ff; mask1 = uctl->mask & 0x00ff; if (uctl->rot_range) { val_l = ROT_RANGE(val_l, mask1); val_r = ROT_RANGE(val_r, mask1); } if (uctl->inv_range) { val_l = INV_RANGE(val_l, mask1); val_r = INV_RANGE(val_r, mask1); } ucontrol->value.integer.value[0] = val_l; ucontrol->value.integer.value[1] = val_r; } else { if (uctl->rot_range) val = ROT_RANGE(val, uctl->mask); if (uctl->inv_range) val = INV_RANGE(val, uctl->mask); ucontrol->value.integer.value[0] = val; } return 0; } static int snd_uda1380_actl_reg_elem_int_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_uda1380 *uda = (struct snd_uda1380 *) kcontrol->private_data; struct snd_uda1380_uctl_reg_elem_int *uctl = (struct snd_uda1380_uctl_reg_elem_int *) kcontrol->private_value; unsigned int val, val_l, val_r, mask1; if (uctl->is_stereo) { val_l = ucontrol->value.integer.value[0]; val_r = ucontrol->value.integer.value[1]; mask1 = uctl->mask & 0x00ff; if (uctl->rot_range) { val_l = ROT_RANGE(val_l, mask1); val_r = ROT_RANGE(val_r, mask1); } if (uctl->inv_range) { val_l = INV_RANGE(val_l, mask1); val_r = INV_RANGE(val_r, mask1); } val = val_l << 8 | val_r; } else { val = ucontrol->value.integer.value[0]; if (uctl->rot_range) val = ROT_RANGE(val, uctl->mask); if (uctl->inv_range) val = INV_RANGE(val, uctl->mask); } snd_uda1380_lock(uda); uda->regs[uctl->reg] = WRITE_MASK(uda->regs[uctl->reg], val << uctl->shift, uctl->mask << uctl->shift); snd_uda1380_cache_dirty(uda, uctl->reg); snd_uda1380_unlock(uda); return 0; } /* declarations of ALSA reg_elem_int controls */ #define ACTL_REG_ELEM_INT(ctl_name, _name, _reg, _shift, _mask, _inv_range, _rot_range) \ static struct snd_uda1380_uctl_reg_elem_int snd_uda1380_actl_ ## ctl_name ## _pvalue = \ { .reg = _reg, .shift = _shift, .mask = _mask, \ .inv_range = _inv_range, .rot_range = _rot_range }; \ static struct snd_kcontrol_new snd_uda1380_actl_ ## ctl_name = \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = _name, .info = snd_uda1380_actl_reg_elem_int_info, \ .get = snd_uda1380_actl_reg_elem_int_get, .put = snd_uda1380_actl_reg_elem_int_put, \ .private_value = (unsigned long)&snd_uda1380_actl_ ## ctl_name ## _pvalue }; #define ACTL_REG_ELEM_INT_STEREO(ctl_name, _name, _reg, _shift, _mask, _inv_range, _rot_range) \ static struct snd_uda1380_uctl_reg_elem_int snd_uda1380_actl_ ## ctl_name ## _pvalue = \ { .is_stereo = 1, .reg = _reg, .shift = _shift, .mask = _mask, \ .inv_range = _inv_range, .rot_range = _rot_range }; \ static struct snd_kcontrol_new snd_uda1380_actl_ ## ctl_name = \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = _name, .info = snd_uda1380_actl_reg_elem_int_info, \ .get = snd_uda1380_actl_reg_elem_int_get, .put = snd_uda1380_actl_reg_elem_int_put, \ .private_value = (unsigned long)&snd_uda1380_actl_ ## ctl_name ## _pvalue };\ ACTL_REG_ELEM_INT_STEREO(playback_volume, "Master Playback Volume", 0x10, 0, 0xffff, 1, 0) ACTL_REG_ELEM_INT(deemphasis, "De-Emphasis", 0x13, 0, 0x0007, 0, 0) ACTL_REG_ELEM_INT(tone_ctl_strength, "Tone Control - Strength", 0x12, 14, 0x0003, 0, 0) ACTL_REG_ELEM_INT_STEREO(tone_ctl_treble, "Tone Control - Treble", 0x12, 4, 0x0303, 0, 0) ACTL_REG_ELEM_INT_STEREO(tone_ctl_bass, "Tone Control - Bass", 0x12, 0, 0x0f0f, 0, 0) ACTL_REG_ELEM_INT(mic_gain, "Mic Capture Volume", 0x22, 8, 0x000f, 0, 0) ACTL_REG_ELEM_INT_STEREO(line_in_gain, "Line Capture Volume", 0x21, 0, 0x0f0f, 0, 0) ACTL_REG_ELEM_INT_STEREO(capture_volume, "Capture Volume", 0x20, 0, 0xffff, 0, 1) struct snd_uda1380_uctl_bool { int (*get) (struct snd_uda1380 *uda); int (*set) (struct snd_uda1380 *uda, int on); }; static int snd_uda1380_actl_bool_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; return 0; } static int snd_uda1380_actl_bool_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_uda1380 *uda = (struct snd_uda1380 *) kcontrol->private_data; struct snd_uda1380_uctl_bool *uctl = (struct snd_uda1380_uctl_bool *) kcontrol->private_value; ucontrol->value.integer.value[0] = uctl->get(uda); return 0; } static int snd_uda1380_actl_bool_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_uda1380 *uda = (struct snd_uda1380 *) kcontrol->private_data; struct snd_uda1380_uctl_bool *uctl = (struct snd_uda1380_uctl_bool *) kcontrol->private_value; return uctl->set(uda, ucontrol->value.integer.value[0]); } /* Register flags */ #define R00_EN_ADC 0x0800 #define R00_EN_DEC 0x0400 #define R00_EN_DAC 0x0200 #define R00_EN_INT 0x0100 #define R02_PON_HP 0x2000 #define R02_PON_DAC 0x0400 #define R02_PON_BIAS 0x0100 #define R02_PON_LNA 0x0010 #define R02_PON_PGAL 0x0008 #define R02_PON_ADCL 0x0004 #define R02_PON_PGAR 0x0002 #define R02_PON_ADCR 0x0001 #define R13_MTM 0x4000 #define R21_MT_ADC 0x8000 #define R22_SEL_LNA 0x0008 #define R22_SEL_MIC 0x0004 #define R22_SKIP_DCFIL 0x0002 #define R23_AGC_EN 0x0001 static int snd_uda1380_uctl_playback_switch_get(struct snd_uda1380 *uda) { return uda->playback_switch_ureq; } static int snd_uda1380_uctl_playback_switch_set(struct snd_uda1380 *uda, int on) { snd_uda1380_lock(uda); uda->playback_switch_ureq = on; if (uda->playback_on) { uda->regs[0x13] = WRITE_MASK(uda->regs[0x13], on ? 0x0000 : R13_MTM, R13_MTM); snd_uda1380_hwsync_write(uda, 0x13, 1); } snd_uda1380_unlock(uda); return 0; } static int snd_uda1380_uctl_capture_switch_get(struct snd_uda1380 *uda) { return uda->capture_switch_ureq; } static int snd_uda1380_uctl_capture_switch_set(struct snd_uda1380 *uda, int on) { snd_uda1380_lock(uda); uda->capture_switch_ureq = on; if (uda->capture_on) { uda->regs[0x21] = WRITE_MASK(uda->regs[0x21], on ? 0x0000 : R21_MT_ADC, R21_MT_ADC); snd_uda1380_hwsync_write(uda, 0x21, 1); } snd_uda1380_unlock(uda); return 0; } static int snd_uda1380_uctl_agc_get(struct snd_uda1380 *uda) { return uda->regs[0x23] & R23_AGC_EN; } static int snd_uda1380_uctl_agc_set (struct snd_uda1380 *uda, int on) { snd_uda1380_lock(uda); uda->regs[0x23] = WRITE_MASK(uda->regs[0x23], on ? R23_AGC_EN : 0x0000, R23_AGC_EN); snd_uda1380_cache_dirty_zone(uda, 0x23, 1); snd_uda1380_unlock(uda); return 0; } #define ACTL_BOOL(ctl_name, _name) \ static struct snd_uda1380_uctl_bool snd_uda1380_actl_ ## ctl_name ## _pvalue = \ { .get = snd_uda1380_uctl_ ## ctl_name ## _get, \ .set = snd_uda1380_uctl_ ## ctl_name ## _set }; \ static struct snd_kcontrol_new snd_uda1380_actl_ ## ctl_name = \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = _name, .info = snd_uda1380_actl_bool_info, \ .get = snd_uda1380_actl_bool_get, .put = snd_uda1380_actl_bool_put, \ .private_value = (unsigned long) &snd_uda1380_actl_ ## ctl_name ## _pvalue }; ACTL_BOOL(playback_switch, "Master Playback Switch") ACTL_BOOL(agc, "AGC") ACTL_BOOL(capture_switch, "Capture Switch") static inline void snd_uda1380_line_out_on(struct snd_uda1380 *uda, int on); void snd_uda1380_hp_connected(struct snd_uda1380 *uda, int connected) { snd_uda1380_lock(uda); if (connected != uda->hp_connected) { uda->hp_connected = connected; if (uda->playback_on) { uda->regs[0x02] = WRITE_MASK(uda->regs[0x02], connected ? R02_PON_HP : 0x0000, R02_PON_HP); snd_uda1380_hwsync_write(uda, 0x02, 1); if (uda->hp_or_line_out) snd_uda1380_line_out_on(uda, !connected); } } snd_uda1380_unlock(uda); } static int snd_uda1380_uctl_select_capture_source(struct snd_uda1380 *uda, enum snd_uda1380_capture_source capture_source) { snd_uda1380_lock(uda); uda->capture_source = capture_source; if ((uda->capture_source == SND_UDA1380_CAP_SOURCE_MIC) == ((uda->regs[0x22] & R22_SEL_MIC) == R22_SEL_MIC)) { snd_uda1380_unlock(uda); return 0; } if (uda->capture_on) { if (uda->capture_source == SND_UDA1380_CAP_SOURCE_MIC) { uda->regs[0x02] = WRITE_MASK(uda->regs[0x02], R02_PON_LNA, R02_PON_LNA); snd_uda1380_hwsync_write(uda, 0x02, 1); uda->regs[0x22] = WRITE_MASK(uda->regs[0x22], R22_SEL_LNA | R22_SEL_MIC, R22_SEL_LNA | R22_SEL_MIC); snd_uda1380_hwsync_write(uda, 0x22, 1); uda->regs[0x02] = WRITE_MASK(uda->regs[0x02], 0x0000, R02_PON_PGAL | R02_PON_PGAR); snd_uda1380_hwsync_write(uda, 0x02, 1); } else { uda->regs[0x02] = WRITE_MASK(uda->regs[0x02], R02_PON_PGAL | R02_PON_PGAR, R02_PON_PGAL | R02_PON_PGAR); snd_uda1380_hwsync_write(uda, 0x02, 1); uda->regs[0x22] = WRITE_MASK(uda->regs[0x22], 0x0000, R22_SEL_LNA | R22_SEL_MIC); snd_uda1380_hwsync_write(uda, 0x22, 1); uda->regs[0x02] = WRITE_MASK(uda->regs[0x02], 0x0000, R02_PON_LNA ); snd_uda1380_hwsync_write(uda, 0x02, 1); } } snd_uda1380_unlock(uda); return 0; } static int snd_uda1380_actl_capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 2; if (uinfo->value.enumerated.item == SND_UDA1380_CAP_SOURCE_LINE_IN) strcpy(uinfo->value.enumerated.name, "Line"); else strcpy(uinfo->value.enumerated.name, "Mic"); return 0; } static int snd_uda1380_actl_capture_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_uda1380 *uda = (struct snd_uda1380 *) kcontrol->private_data; ucontrol->value.enumerated.item[0] = uda->capture_source; return 0; } static int snd_uda1380_actl_capture_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_uda1380 *uda = (struct snd_uda1380 *) kcontrol->private_data; return snd_uda1380_uctl_select_capture_source(uda, ucontrol->value.enumerated.item[0]); } static struct snd_kcontrol_new snd_uda1380_actl_capture_source = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Source", .info = snd_uda1380_actl_capture_source_info, .get = snd_uda1380_actl_capture_source_get, .put = snd_uda1380_actl_capture_source_put }; /* end {{ Controls }} */ /* begin {{ Headphone Detected Notification }} */ static void snd_uda1380_hp_detected_w_fn(void *p) { struct snd_uda1380 *uda = (struct snd_uda1380 *)p; snd_uda1380_hp_connected(uda, uda->hp_detected.detected); } void snd_uda1380_hp_detected(struct snd_uda1380 *uda, int detected) { if (detected != uda->hp_detected.detected) { uda->hp_detected.detected = detected; queue_work(uda->hp_detected.wq, &uda->hp_detected.w); } } static int snd_uda1380_hp_detected_init(struct snd_uda1380 *uda) { INIT_WORK(&uda->hp_detected.w, snd_uda1380_hp_detected_w_fn, uda); uda->hp_detected.detected = uda->hp_connected; uda->hp_detected.wq = create_singlethread_workqueue("uda1380"); if (uda->hp_detected.wq) return 0; else return -1; } static void snd_uda1380_hp_detected_free(struct snd_uda1380 *uda) { destroy_workqueue(uda->hp_detected.wq); } /* end {{ Headphone Detected Notification }} */ /* begin {{ Codec Control }} */ static inline int _wait_mute(struct snd_uda1380 *uda, u8 reg, int on) { #define MUTE_STATE 0x0004 unsigned int timeout_count = 50; snd_uda1380_hwsync_read(uda, reg, 1); while (((uda->regs[reg] & MUTE_STATE) == MUTE_STATE) != on) { if (--timeout_count == 0) break; msleep(1); snd_uda1380_hwsync_read(uda, reg, 1); } return (timeout_count) ? 0 : -1; } static inline int snd_uda1380_power_on(struct snd_uda1380 *uda) { uda->power_on_chip(1); uda->reset_pin(1); mdelay(1); uda->reset_pin(0); uda->powered_on = 1; return 0; } static inline int snd_uda1380_power_off(struct snd_uda1380 *uda) { uda->powered_on = 0; uda->power_on_chip(0); return 0; } static inline void snd_uda1380_line_out_on(struct snd_uda1380 *uda, int on) { if (uda->line_out_on) uda->line_out_on(on); } static inline void snd_uda1380_mic_on(struct snd_uda1380 *uda, int on) { if (uda->mic_on) uda->mic_on(on); } static inline void snd_uda1380_line_in_on(struct snd_uda1380 *uda, int on) { if (uda->line_in_on) uda->line_in_on(on); } static int snd_uda1380_playback_on(struct snd_uda1380 *uda) { unsigned int val; if (uda->playback_on) return 0; /* power-up */ uda->regs[0x00] = WRITE_MASK(uda->regs[0x00], R00_EN_DAC | R00_EN_INT, R00_EN_DAC | R00_EN_INT); val = R02_PON_DAC; val |= uda->hp_connected ? R02_PON_HP : 0x0000; uda->regs[0x02] = WRITE_MASK(uda->regs[0x02], val, R02_PON_HP | R02_PON_DAC); snd_uda1380_hwsync_write(uda, 0x00, 3); uda->playback_clock_on = 1; snd_uda1380_cache_try_flush(uda); /* notify line out is on */ if (!(uda->hp_or_line_out && uda->hp_connected)) snd_uda1380_line_out_on(uda, 1); /* unmute, in case */ if (uda->playback_switch_ureq) { uda->regs[0x13] = WRITE_MASK(uda->regs[0x13], 0x0000, R13_MTM); snd_uda1380_hwsync_write(uda, 0x13, 1); _wait_mute(uda, 0x18, 0); } uda->playback_on = 1; return 0; } static int snd_uda1380_playback_off(struct snd_uda1380 *uda) { if (!uda->playback_on) return 0; uda->playback_on = 0; /* mute */ if (!(uda->regs[0x13] & R13_MTM)) { uda->regs[0x13] = WRITE_MASK(uda->regs[0x13], R13_MTM, R13_MTM); snd_uda1380_hwsync_write(uda, 0x13, 1); } _wait_mute(uda, 0x18, 1); /* notify line out going off */ if (!(uda->hp_or_line_out && uda->hp_connected)) snd_uda1380_line_out_on(uda, 0); /* power-down */ uda->playback_clock_on = 0; uda->regs[0x00] = WRITE_MASK(uda->regs[0x00], 0x0000, R00_EN_DAC | R00_EN_INT); uda->regs[0x02] = WRITE_MASK(uda->regs[0x02], 0x0000, R02_PON_HP | R02_PON_DAC); snd_uda1380_hwsync_write(uda, 0x00, 3); return 0; } static int snd_uda1380_capture_on(struct snd_uda1380 *uda) { unsigned int val; if (uda->capture_on) return 0; /* power-up */ uda->regs[0x00] = WRITE_MASK(uda->regs[0x00], R00_EN_ADC | R00_EN_DEC, R00_EN_ADC | R00_EN_DEC); snd_uda1380_hwsync_write(uda, 0x00, 1); uda->capture_clock_on = 1; snd_uda1380_cache_try_flush(uda); val = R02_PON_ADCL | R02_PON_ADCR; val |= (uda->regs[0x22] & R22_SEL_MIC) ? R02_PON_LNA : R02_PON_PGAL | R02_PON_PGAR; uda->regs[0x02] = WRITE_MASK(uda->regs[0x02], val, R02_PON_ADCL | R02_PON_ADCR | R02_PON_LNA | R02_PON_PGAL | R02_PON_PGAR); snd_uda1380_hwsync_write(uda, 0x02, 1); /* notify input sources */ if (uda->regs[0x22] & R22_SEL_MIC) snd_uda1380_mic_on(uda, 1); else snd_uda1380_line_in_on(uda, 1); /* unmute, in case */ if (uda->playback_switch_ureq) { uda->regs[0x21] = WRITE_MASK(uda->regs[0x21], 0x0000, R21_MT_ADC); snd_uda1380_hwsync_write(uda, 0x21, 1); _wait_mute(uda, 0x28, 0); } uda->capture_on = 1; return 0; } static int snd_uda1380_capture_off(struct snd_uda1380 *uda) { if (!uda->capture_on) return 0; uda->capture_on = 0; /* mute */ if (!(uda->regs[0x21] & R21_MT_ADC)) { uda->regs[0x21] = WRITE_MASK(uda->regs[0x21], R21_MT_ADC, R21_MT_ADC); snd_uda1380_hwsync_write(uda, 0x21, 1); } _wait_mute(uda, 0x28, 1); /* notify input sources going off */ if (uda->regs[0x22] & R22_SEL_MIC) snd_uda1380_mic_on(uda, 0); else snd_uda1380_line_in_on(uda, 0); /* power-down */ uda->capture_clock_on = 0; uda->regs[0x00] = WRITE_MASK(uda->regs[0x00], 0x0000, R00_EN_ADC | R00_EN_DEC); uda->regs[0x02] = WRITE_MASK(uda->regs[0x02], 0x0000, R02_PON_ADCL | R02_PON_ADCR | R02_PON_LNA | R02_PON_PGAL | R02_PON_PGAR); snd_uda1380_hwsync_write(uda, 0x00, 3); return 0; } int snd_uda1380_open_stream(struct snd_uda1380 *uda, int stream) { snd_uda1380_lock(uda); if (stream == SNDRV_PCM_STREAM_PLAYBACK) { uda->playback_stream_opened = 1; snd_uda1380_playback_on(uda); } else { uda->capture_stream_opened = 1; snd_uda1380_capture_on(uda); } snd_uda1380_unlock(uda); return 0; } int snd_uda1380_close_stream(struct snd_uda1380 *uda, int stream) { snd_uda1380_lock(uda); if (stream == SNDRV_PCM_STREAM_PLAYBACK) { uda->playback_stream_opened = 0; snd_uda1380_playback_off(uda); } else { uda->capture_stream_opened = 0; snd_uda1380_capture_off(uda); } snd_uda1380_unlock(uda); return 0; } static int snd_uda1380_init_regs(struct snd_uda1380 *uda) { snd_uda1380_hwsync_read(uda, 0x00, 5); snd_uda1380_hwsync_read(uda, 0x10, 5); snd_uda1380_hwsync_read(uda, 0x20, 4); //@@ MEMO: add some configs if (uda->capture_source == SND_UDA1380_CAP_SOURCE_MIC) { uda->regs[0x22] = WRITE_MASK(uda->regs[0x22], R22_SEL_LNA | R22_SEL_MIC, R22_SEL_LNA | R22_SEL_MIC); } uda->regs[0x22] = WRITE_MASK(uda->regs[0x22], 0x0000, R22_SKIP_DCFIL); snd_uda1380_hwsync_write(uda, 0x22, 1); uda->regs[0x00] = WRITE_MASK(uda->regs[0x00], 0x0000, R00_EN_DEC | R00_EN_INT); uda->regs[0x02] = WRITE_MASK(uda->regs[0x02], R02_PON_BIAS, R02_PON_BIAS); snd_uda1380_hwsync_write(uda, 0x00, 3); return 0; } int snd_uda1380_suspend(struct snd_uda1380 *uda, pm_message_t state) { snd_uda1380_lock(uda); if (uda->playback_on) snd_uda1380_playback_off(uda); if (uda->capture_on) snd_uda1380_capture_off(uda); snd_uda1380_power_off(uda); snd_uda1380_cache_dirty_all(uda); snd_uda1380_unlock(uda); return 0; } int snd_uda1380_resume(struct snd_uda1380 *uda) { snd_uda1380_lock(uda); snd_uda1380_power_on(uda); snd_uda1380_cache_try_flush(uda); if (uda->playback_stream_opened) snd_uda1380_playback_on(uda); if (uda->capture_stream_opened) snd_uda1380_capture_on(uda); snd_uda1380_unlock(uda); return 0; } static void snd_uda1380_init_uda(struct snd_uda1380 *uda) { init_MUTEX(&uda->sem); uda->i2c_client.driver = &snd_uda1380_i2c_driver; } int snd_uda1380_activate(struct snd_uda1380 *uda) { int ret = 0; snd_uda1380_init_uda(uda); snd_uda1380_lock(uda); snd_uda1380_power_on(uda); if ((ret = snd_uda1380_i2c_attach(uda)) < 0) goto failed_i2c_attach; snd_uda1380_init_regs(uda); if ((ret = snd_uda1380_hp_detected_init(uda)) < 0) goto failed_hp_detected_init; snd_uda1380_unlock(uda); return 0; failed_hp_detected_init: snd_uda1380_i2c_detach(uda); failed_i2c_attach: snd_uda1380_power_off(uda); snd_uda1380_unlock(uda); return ret; } void snd_uda1380_deactivate(struct snd_uda1380 *uda) { snd_uda1380_lock(uda); snd_uda1380_hp_detected_free(uda); snd_uda1380_i2c_detach(uda); snd_uda1380_power_off(uda); snd_uda1380_unlock(uda); } int snd_uda1380_add_mixer_controls(struct snd_uda1380 *uda, struct snd_card *card) { snd_uda1380_lock(uda); snd_ctl_add(card, snd_ctl_new1(&snd_uda1380_actl_playback_volume, uda)); snd_ctl_add(card, snd_ctl_new1(&snd_uda1380_actl_playback_switch, uda)); snd_ctl_add(card, snd_ctl_new1(&snd_uda1380_actl_deemphasis, uda)); snd_ctl_add(card, snd_ctl_new1(&snd_uda1380_actl_tone_ctl_strength, uda)); snd_ctl_add(card, snd_ctl_new1(&snd_uda1380_actl_tone_ctl_treble, uda)); snd_ctl_add(card, snd_ctl_new1(&snd_uda1380_actl_tone_ctl_bass, uda)); snd_ctl_add(card, snd_ctl_new1(&snd_uda1380_actl_capture_volume, uda)); snd_ctl_add(card, snd_ctl_new1(&snd_uda1380_actl_capture_switch, uda)); if (uda->mic_connected) snd_ctl_add(card, snd_ctl_new1(&snd_uda1380_actl_mic_gain, uda)); if (uda->line_in_connected) snd_ctl_add(card, snd_ctl_new1(&snd_uda1380_actl_line_in_gain, uda)); if (uda->mic_connected && uda->line_in_connected) snd_ctl_add(card, snd_ctl_new1(&snd_uda1380_actl_capture_source, uda)); snd_ctl_add(card, snd_ctl_new1(&snd_uda1380_actl_agc, uda)); snd_uda1380_unlock(uda); return 0; } /* end {{ Codec Control }} */ /* begin {{ Module }} */ static int __init snd_uda1380_module_on_load(void) { snd_uda1380_i2c_init(); return 0; } static void __exit snd_uda1380_module_on_unload(void) { snd_uda1380_i2c_free(); } module_init(snd_uda1380_module_on_load); module_exit(snd_uda1380_module_on_unload); EXPORT_SYMBOL(snd_uda1380_activate); EXPORT_SYMBOL(snd_uda1380_deactivate); EXPORT_SYMBOL(snd_uda1380_add_mixer_controls); EXPORT_SYMBOL(snd_uda1380_open_stream); EXPORT_SYMBOL(snd_uda1380_close_stream); EXPORT_SYMBOL(snd_uda1380_suspend); EXPORT_SYMBOL(snd_uda1380_resume); EXPORT_SYMBOL(snd_uda1380_hp_connected); EXPORT_SYMBOL(snd_uda1380_hp_detected); MODULE_AUTHOR("Giorgio Padrin"); MODULE_DESCRIPTION("Audio support for codec Philips UDA1380"); MODULE_LICENSE("GPL"); /* end {{ Module }} */