📄 pontis.c
字号:
/* * ALSA driver for ICEnsemble VT1724 (Envy24HT) * * Lowlevel functions for Pontis MS300 * * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <sound/driver.h>#include <asm/io.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/slab.h>#include <sound/core.h>#include <sound/info.h>#include "ice1712.h"#include "envy24ht.h"#include "pontis.h"/* I2C addresses */#define WM_DEV 0x34#define CS_DEV 0x20/* WM8776 registers */#define WM_HP_ATTEN_L 0x00 /* headphone left attenuation */#define WM_HP_ATTEN_R 0x01 /* headphone left attenuation */#define WM_HP_MASTER 0x02 /* headphone master (both channels), override LLR */#define WM_DAC_ATTEN_L 0x03 /* digital left attenuation */#define WM_DAC_ATTEN_R 0x04#define WM_DAC_MASTER 0x05#define WM_PHASE_SWAP 0x06 /* DAC phase swap */#define WM_DAC_CTRL1 0x07#define WM_DAC_MUTE 0x08#define WM_DAC_CTRL2 0x09#define WM_DAC_INT 0x0a#define WM_ADC_INT 0x0b#define WM_MASTER_CTRL 0x0c#define WM_POWERDOWN 0x0d#define WM_ADC_ATTEN_L 0x0e#define WM_ADC_ATTEN_R 0x0f#define WM_ALC_CTRL1 0x10#define WM_ALC_CTRL2 0x11#define WM_ALC_CTRL3 0x12#define WM_NOISE_GATE 0x13#define WM_LIMITER 0x14#define WM_ADC_MUX 0x15#define WM_OUT_MUX 0x16#define WM_RESET 0x17/* * GPIO */#define PONTIS_CS_CS (1<<4) /* CS */#define PONTIS_CS_CLK (1<<5) /* CLK */#define PONTIS_CS_RDATA (1<<6) /* CS8416 -> VT1720 */#define PONTIS_CS_WDATA (1<<7) /* VT1720 -> CS8416 *//* * get the current register value of WM codec */static unsigned short wm_get(ice1712_t *ice, int reg){ reg <<= 1; return ((unsigned short)ice->akm[0].images[reg] << 8) | ice->akm[0].images[reg + 1];}/* * set the register value of WM codec and remember it */static void wm_put_nocache(ice1712_t *ice, int reg, unsigned short val){ unsigned short cval; cval = (reg << 9) | val; snd_vt1724_write_i2c(ice, WM_DEV, cval >> 8, cval & 0xff);}static void wm_put(ice1712_t *ice, int reg, unsigned short val){ wm_put_nocache(ice, reg, val); reg <<= 1; ice->akm[0].images[reg] = val >> 8; ice->akm[0].images[reg + 1] = val;}/* * DAC volume attenuation mixer control (-64dB to 0dB) */#define DAC_0dB 0xff#define DAC_RES 128#define DAC_MIN (DAC_0dB - DAC_RES)static int wm_dac_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = 0; /* mute */ uinfo->value.integer.max = DAC_RES; /* 0dB, 0.5dB step */ return 0;}static int wm_dac_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ ice1712_t *ice = snd_kcontrol_chip(kcontrol); unsigned short val; int i; down(&ice->gpio_mutex); for (i = 0; i < 2; i++) { val = wm_get(ice, WM_DAC_ATTEN_L + i) & 0xff; val = val > DAC_MIN ? (val - DAC_MIN) : 0; ucontrol->value.integer.value[i] = val; } up(&ice->gpio_mutex); return 0;}static int wm_dac_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ ice1712_t *ice = snd_kcontrol_chip(kcontrol); unsigned short oval, nval; int i, idx, change = 0; down(&ice->gpio_mutex); for (i = 0; i < 2; i++) { nval = ucontrol->value.integer.value[i]; nval = (nval ? (nval + DAC_MIN) : 0) & 0xff; idx = WM_DAC_ATTEN_L + i; oval = wm_get(ice, idx) & 0xff; if (oval != nval) { wm_put(ice, idx, nval); wm_put_nocache(ice, idx, nval | 0x100); change = 1; } } up(&ice->gpio_mutex); return change;}/* * ADC gain mixer control (-64dB to 0dB) */#define ADC_0dB 0xcf#define ADC_RES 128#define ADC_MIN (ADC_0dB - ADC_RES)static int wm_adc_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = 0; /* mute (-64dB) */ uinfo->value.integer.max = ADC_RES; /* 0dB, 0.5dB step */ return 0;}static int wm_adc_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ ice1712_t *ice = snd_kcontrol_chip(kcontrol); unsigned short val; int i; down(&ice->gpio_mutex); for (i = 0; i < 2; i++) { val = wm_get(ice, WM_ADC_ATTEN_L + i) & 0xff; val = val > ADC_MIN ? (val - ADC_MIN) : 0; ucontrol->value.integer.value[i] = val; } up(&ice->gpio_mutex); return 0;}static int wm_adc_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol){ ice1712_t *ice = snd_kcontrol_chip(kcontrol); unsigned short ovol, nvol; int i, idx, change = 0; down(&ice->gpio_mutex); for (i = 0; i < 2; i++) { nvol = ucontrol->value.integer.value[i]; nvol = nvol ? (nvol + ADC_MIN) : 0; idx = WM_ADC_ATTEN_L + i; ovol = wm_get(ice, idx) & 0xff; if (ovol != nvol) { wm_put(ice, idx, nvol); change = 1; } } up(&ice->gpio_mutex); return change;}/* * ADC input mux mixer control */static int wm_adc_mux_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; return 0;}static int wm_adc_mux_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol){ ice1712_t *ice = snd_kcontrol_chip(kcontrol); int bit = kcontrol->private_value; down(&ice->gpio_mutex); ucontrol->value.integer.value[0] = (wm_get(ice, WM_ADC_MUX) & (1 << bit)) ? 1 : 0; up(&ice->gpio_mutex); return 0;}static int wm_adc_mux_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol){ ice1712_t *ice = snd_kcontrol_chip(kcontrol); int bit = kcontrol->private_value; unsigned short oval, nval; int change; down(&ice->gpio_mutex); nval = oval = wm_get(ice, WM_ADC_MUX); if (ucontrol->value.integer.value[0]) nval |= (1 << bit); else nval &= ~(1 << bit); change = nval != oval; if (change) { wm_put(ice, WM_ADC_MUX, nval); } up(&ice->gpio_mutex); return 0;}/* * Analog bypass (In -> Out) */static int wm_bypass_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; return 0;}static int wm_bypass_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol){ ice1712_t *ice = snd_kcontrol_chip(kcontrol); down(&ice->gpio_mutex); ucontrol->value.integer.value[0] = (wm_get(ice, WM_OUT_MUX) & 0x04) ? 1 : 0; up(&ice->gpio_mutex); return 0;}static int wm_bypass_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol){ ice1712_t *ice = snd_kcontrol_chip(kcontrol); unsigned short val, oval; int change = 0; down(&ice->gpio_mutex); val = oval = wm_get(ice, WM_OUT_MUX); if (ucontrol->value.integer.value[0]) val |= 0x04; else val &= ~0x04; if (val != oval) { wm_put(ice, WM_OUT_MUX, val); change = 1; } up(&ice->gpio_mutex); return change;}/* * Left/Right swap */static int wm_chswap_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; return 0;}static int wm_chswap_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol){ ice1712_t *ice = snd_kcontrol_chip(kcontrol); down(&ice->gpio_mutex); ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL1) & 0xf0) != 0x90; up(&ice->gpio_mutex); return 0;}static int wm_chswap_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol){ ice1712_t *ice = snd_kcontrol_chip(kcontrol); unsigned short val, oval; int change = 0; down(&ice->gpio_mutex); oval = wm_get(ice, WM_DAC_CTRL1); val = oval & 0x0f; if (ucontrol->value.integer.value[0]) val |= 0x60; else val |= 0x90; if (val != oval) { wm_put(ice, WM_DAC_CTRL1, val); wm_put_nocache(ice, WM_DAC_CTRL1, val); change = 1; } up(&ice->gpio_mutex); return change;}/* * write data in the SPI mode */static void set_gpio_bit(ice1712_t *ice, unsigned int bit, int val){ unsigned int tmp = snd_ice1712_gpio_read(ice); if (val) tmp |= bit; else tmp &= ~bit; snd_ice1712_gpio_write(ice, tmp);}static void spi_send_byte(ice1712_t *ice, unsigned char data){ int i; for (i = 0; i < 8; i++) { set_gpio_bit(ice, PONTIS_CS_CLK, 0); udelay(1); set_gpio_bit(ice, PONTIS_CS_WDATA, data & 0x80); udelay(1); set_gpio_bit(ice, PONTIS_CS_CLK, 1); udelay(1); data <<= 1; }}static unsigned int spi_read_byte(ice1712_t *ice){ int i; unsigned int val = 0; for (i = 0; i < 8; i++) { val <<= 1; set_gpio_bit(ice, PONTIS_CS_CLK, 0); udelay(1); if (snd_ice1712_gpio_read(ice) & PONTIS_CS_RDATA) val |= 1; udelay(1); set_gpio_bit(ice, PONTIS_CS_CLK, 1); udelay(1); } return val;}static void spi_write(ice1712_t *ice, unsigned int dev, unsigned int reg, unsigned int data){ snd_ice1712_gpio_set_dir(ice, PONTIS_CS_CS|PONTIS_CS_WDATA|PONTIS_CS_CLK); snd_ice1712_gpio_set_mask(ice, ~(PONTIS_CS_CS|PONTIS_CS_WDATA|PONTIS_CS_CLK)); set_gpio_bit(ice, PONTIS_CS_CS, 0); spi_send_byte(ice, dev & ~1); /* WRITE */ spi_send_byte(ice, reg); /* MAP */ spi_send_byte(ice, data); /* DATA */ /* trigger */ set_gpio_bit(ice, PONTIS_CS_CS, 1); udelay(1); /* restore */ snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask); snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);}static unsigned int spi_read(ice1712_t *ice, unsigned int dev, unsigned int reg){ unsigned int val; snd_ice1712_gpio_set_dir(ice, PONTIS_CS_CS|PONTIS_CS_WDATA|PONTIS_CS_CLK); snd_ice1712_gpio_set_mask(ice, ~(PONTIS_CS_CS|PONTIS_CS_WDATA|PONTIS_CS_CLK)); set_gpio_bit(ice, PONTIS_CS_CS, 0); spi_send_byte(ice, dev & ~1); /* WRITE */ spi_send_byte(ice, reg); /* MAP */ /* trigger */ set_gpio_bit(ice, PONTIS_CS_CS, 1); udelay(1); set_gpio_bit(ice, PONTIS_CS_CS, 0); spi_send_byte(ice, dev | 1); /* READ */ val = spi_read_byte(ice); /* trigger */ set_gpio_bit(ice, PONTIS_CS_CS, 1); udelay(1); /* restore */ snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask); snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); return val;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -