📄 wm8976.c
字号:
/* * Routines for control of the TEA6330T circuit via i2c bus * Sound fader control circuit for car radios by Philips Semiconductors * Copyright (c) by Jaroslav Kysela <perex@suse.cz> * * * 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 <linux/init.h>#include <linux/slab.h>#include <sound/core.h>#include <sound/wm8976.h>#include<asm/arch-x900/apb_i2c_module.h>#include <linux/delay.h>#include <sound/info.h>MODULE_AUTHOR("yolanda <zouzf@hotmail.com>");MODULE_DESCRIPTION("Routines for control of the wm8976 circuit via i2c bus");MODULE_LICENSE("GPL");#define WM8976_ADDR (0x34>>1) /* fixed address */#define PIN_MASKBASE 0x20020000static unsigned char script_wm8976[][3] = //Lout1 SLAVE MODE with pll mckl=27MHz{ {0x34,0x00,0x00},//R0 0X000 {0x34,0x02,0x2d},//R1 0X02D {0x34,0x05,0x91},//R2 0X191 {0x34,0x06,0x0F},//R3 0X00F {0x34,0x08,0x10},//R4 0X010 {0x34,0x0a,0x00},//R5 0X000#ifdef SLAVEMODE {0x34,0x0d,0x4d},//R6 0X14d#else {0x34,0x0d,0x4c},//R6 0X14c //set pll out#endif {0x34,0x0e,0x00},//R7 0X00A //set sample rate 48khz {0x34,0x17,0xff},//R11 0X1ff {0x34,0x19,0xff},//R12 0X1ff {0x34,0x1F,0xff},//R15 0X1FF {0x34,0x48,0x17},//R36 0X017 {0x34,0x4A,0x12},//R37 0X012 {0x34,0x4C,0x11},//R38 0X011 {0x34,0x4E,0x96},//R39 0X096 //set pll 12.288Mhz {0x34,0x58,0x00},//R44 0X000 {0x34,0x5f,0x55},//R47 0X005 {0X34,0x62,0x02},//R49 0X002 {0x34,0x64,0x01},//R50 0X001 {0x34,0x66,0x01},//R51 0X001 {0x34,0x69,0x3f},//R52 0X13f {0x34,0x6b,0x3f},//R53 0X13f};static int PinMask( int device , int enable ){ u32 mask; int error = 0; switch( device ) { case 0: // disable 656 out ( 68k clock ). mask = (1<<14); break; case 1: // i2c out mask = 0x00000480; break; case 2: mask = (1<<21); // disable 656 out ( sensor clock enable) break; case 3: // 68k enable( sci disable ) mask = (1<<15); break; default: error = 1; } if( mask ) { volatile int* pinmark = (volatile int*)ioremap( PIN_MASKBASE , 0x1000); if( pinmark ) { if( enable ) pinmark[0x10c/4] |= mask; else pinmark[0x10c/4] &= ~mask; iounmap( (void*)pinmark ); } else { error = 2; } } return error;}unsigned char wm8976_for_close={0x34,0x02,0x0}; void jade_open(void){ unsigned int count,i; i2cInst* t; PinMask( 1 , 1 ); // enable i2c pin mask t =init_i2c(); count= sizeof(script_wm8976) / sizeof(script_wm8976[0]); for( i=0; i<count; i++ ) transmitData( 2 , &script_wm8976[i][1] , script_wm8976[i][0]/2 ); /* Wait for the wm8976 to wake up */ mdelay(1); dele_i2c(t); }void jade_close(wm8976_t *wm8976){ i2cInst* t; unsigned char *wm8976_for_close={0x34,0x02,0x0}; t = init_i2c(); transmitData( 2 , &wm8976_for_close[1], 0x34/2 ); mdelay(1); dele_i2c(t);}void jade_command(wm8976_t *wm8976,unsigned char * buffer){ i2cInst* t; t=init_i2c(); transmitData( 2 , &buffer, 0x34/2 ); mdelay(1); dele_i2c(t);}#define WM8976_MODE(xname, xindex) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_wm8976_info_mode, \ .get = snd_wm8976_get_mode, .put = snd_wm8976_put_mode }static int snd_wm8976_info_mode(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 snd_wm8976_get_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ wm8976_t *wm = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = wm->mode; return 0;}static int snd_wm8976_put_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ wm8976_t *wm = snd_kcontrol_chip(kcontrol); int change, count; unsigned char bytes[2]; unsigned char val1; val1 =ucontrol->value.integer.value[0] ; change = val1 != wm->mode; wm->mode = val1; count = 0; bytes[count++] = CLK_GEN<<1; if (change) bytes[count++] = 0x4c |(0 ^ val1); jade_command(wm,bytes ); return change;}#define WM8976_BASS(xname, xindex) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_wm8976_info_bass, \ .get = snd_wm8976_get_bass, .put = snd_wm8976_put_bass }static int snd_wm8976_info_bass(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 snd_wm8976_get_bass(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ wm8976_t *wm = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = wm->bass; return 0;}static int snd_wm8976_put_bass(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ wm8976_t *wm = snd_kcontrol_chip(kcontrol); int change, count; unsigned char bytes_l[2]; unsigned char bytes_r[2]; unsigned char val1; val1 =ucontrol->value.integer.value[0] ; change = val1 != wm->bass; wm->bass = val1; count = 0; bytes_l[count++] = LEFT_MIXER_CTL<<1; bytes_r[count++] =RIGHT_MIXER_CTL<<1; if (change) { bytes_l[count++] = val1; bytes_r[count++] = val1; jade_command(wm,bytes_l ); jade_command(wm,bytes_r ); } return change;}/* {{{ Proc interface */static void snd_wm8976_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer){ wm8976_t *clnt = entry->private_data; wm8976_t *uda = clnt->driver_data; snd_iprintf(buffer, "%s\n\n", uda->card->longname);}static void __devinit snd_wm8976_proc_init(snd_card_t *card, struct wm8976_t *clnt){ snd_info_entry_t *entry; if (! snd_card_proc_new(card, "wm8976", &entry)) snd_info_set_text_ops(entry, clnt, 1024, snd_wm8976_proc_read); }/* }}} */#define WM8976_PLAYBACK_VOL(xname, xindex) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_wm8976_info_playvol, \ .get = snd_wm8976_get_playvol, .put = snd_wm8976_put_playvol}static int snd_wm8976_info_playvol(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; uinfo->value.integer.max =9; return 0;}static int snd_wm8976_get_playvol(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ wm8976_t *wm = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = wm->play_vol; }static int snd_wm8976_put_playvol(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ wm8976_t *wm = snd_kcontrol_chip(kcontrol); int change, count; unsigned char bytes_l[2]; unsigned char bytes_r[2]; unsigned char val1; val1 =ucontrol->value.integer.value[0] ; change = val1 != wm->play_vol; if (change) { if (val1 > wm->play_vol) wm->play_vol =wm->play_vol << 1; else wm->play_vol =wm->play_vol >> 1; count = 0; bytes_l[count++] = LDAC_VOL<<1 | 0x01; bytes_r[count++] =RDAC_VOL<<1 | 0x01; bytes_l[count++] = wm->play_vol ; bytes_r[count++] = wm->play_vol ; jade_command(wm,bytes_l); jade_command(wm,bytes_r); } return change;}#define WM8976_CAPTURE_VOL(xname, xindex) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_wm8976_info_capvol, \ .get = snd_wm8976_get_capvol, .put = snd_wm8976_put_capvol}static int snd_wm8976_info_capvol(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; uinfo->value.integer.max =9; return 0;}static int snd_wm8976_get_capvol(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ wm8976_t *wm = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = wm->record_vol; }static int snd_wm8976_put_capvol(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ wm8976_t *wm = snd_kcontrol_chip(kcontrol); int change, count; unsigned char bytes[2]; unsigned char val1; val1 = ucontrol->value.integer.value[0]; change = val1 != wm->play_vol; if (change) { wm->record_vol = val1; if (val1 > wm->record_vol) wm->record_vol =wm->record_vol << 1; else wm->record_vol =wm->record_vol >> 1; count = 0; bytes[count++] = ADC_VOL<<1 | 0x01; bytes[count++] = wm->record_vol; jade_command(wm,bytes); } return change;}#define WM8976_RESET(xname, xindex) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_wm8976_info_reset, \ .get = snd_wm8976_get_reset, .put = snd_wm8976_put_reset}static int snd_wm8976_info_reset(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 wm8976_dev_free(snd_device_t *device){ struct wm8976_t *clnt = device->device_data; kfree(clnt); return 0;}static int snd_wm8976_get_reset(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ wm8976_t *wm = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = wm->reset; return 0;}static int snd_wm8976_put_reset(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ wm8976_t *wm = snd_kcontrol_chip(kcontrol); int change, count, err; unsigned char bytes[3]; unsigned char val1; val1 =ucontrol->value.integer.value[0] ; change = val1 != wm->reset; wm->reset = val1; count = 0; bytes[count++] = SOFT_RESET<<1; if (change) { bytes[count++] = val1; jade_command(wm,bytes); } return change;}static snd_kcontrol_new_t snd_wm8976_controls[] = { WM8976_MODE("Master Slave Switch", 0), WM8976_BASS("Bypass mode change", 0), WM8976_PLAYBACK_VOL("Playback Vol control", 0), WM8976_CAPTURE_VOL("Capture Vol control", 0),// WM8976_SET_FORMAT("Digital Format ",0), WM8976_RESET(" Soft Reset Codec",0)};int snd_chip_wm8976_mixer_new(snd_card_t *card,struct wm8976_t **clnt){ static snd_device_ops_t ops = { .dev_free = wm8976_dev_free, }; wm8976_t *wm8976; int idx, err; snd_assert(card != NULL, return -EINVAL); wm8976 = kzalloc(sizeof(*wm8976), GFP_KERNEL); if (wm8976 == NULL) return -ENOMEM; if ((err = snd_device_new(card, SNDRV_DEV_CODEC, wm8976, &ops)) < 0) { kfree(wm8976); return err; } for (idx = 0; idx < ARRAY_SIZE(snd_wm8976_controls); idx++) { if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_wm8976_controls[idx], wm8976))) < 0) return err; } *clnt = wm8976; strcpy(card->mixername, "WM8976 Mixer"); ((wm8976_t *)wm8976->driver_data)->card = card; snd_wm8976_proc_init(card, wm8976); return 0;}EXPORT_SYMBOL(snd_chip_wm8976_mixer_new);EXPORT_SYMBOL(jade_open);EXPORT_SYMBOL(jade_close);EXPORT_SYMBOL(jade_command);/* * INIT part */static int __init alsa_wm8976_init(void){ return 0;}static void __exit alsa_wm8976_exit(void){}module_init(alsa_wm8976_init)module_exit(alsa_wm8976_exit)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -