--- arch/arm/Kconfig | 2 arch/arm/mach-pxa/Kconfig | 89 + arch/arm/mach-pxa/Makefile | 1 arch/arm/mach-pxa/generic.c | 13 arch/arm/mach-pxa/htcuniversal/Makefile | 19 arch/arm/mach-pxa/htcuniversal/htcuniversal.c | 468 +++++ arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.c | 917 +++++++++++ arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.h | 65 arch/arm/mach-pxa/htcuniversal/htcuniversal_asic3_leds.c | 143 + arch/arm/mach-pxa/htcuniversal/htcuniversal_bl.c | 61 arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.c | 135 + arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.h | 17 arch/arm/mach-pxa/htcuniversal/htcuniversal_buttons.c | 87 + arch/arm/mach-pxa/htcuniversal/htcuniversal_core.c | 226 ++ arch/arm/mach-pxa/htcuniversal/htcuniversal_lcd.c | 212 ++ arch/arm/mach-pxa/htcuniversal/htcuniversal_phone.c | 167 ++ arch/arm/mach-pxa/htcuniversal/htcuniversal_phone.h | 16 arch/arm/mach-pxa/htcuniversal/htcuniversal_pm.c | 69 arch/arm/mach-pxa/htcuniversal/htcuniversal_power2.c | 97 + arch/arm/mach-pxa/htcuniversal/htcuniversal_ts2.c | 490 ++++++ arch/arm/mach-pxa/htcuniversal/htcuniversal_udc.c | 71 arch/arm/mach-pxa/htcuniversal/tsc2046_ts.h | 20 drivers/input/keyboard/Kconfig | 7 drivers/input/keyboard/Makefile | 1 drivers/input/keyboard/asic3_keys.c | 131 + drivers/leds/Kconfig | 7 drivers/leds/Makefile | 1 drivers/leds/leds-asic3.c | 189 ++ drivers/mfd/Kconfig | 10 drivers/mfd/Makefile | 2 drivers/mfd/asic3_base.c | 1208 +++++++++++++++ drivers/mfd/soc-core.c | 106 + drivers/mfd/soc-core.h | 30 drivers/mmc/host/Kconfig | 6 drivers/mmc/host/Makefile | 1 drivers/mmc/host/asic3_mmc.c | 900 +++++++++++ drivers/mmc/host/asic3_mmc.h | 25 drivers/serial/pxa.c | 22 include/asm-arm/arch-pxa/clock.h | 27 include/asm-arm/arch-pxa/htcuniversal-asic.h | 213 ++ include/asm-arm/arch-pxa/htcuniversal-gpio.h | 220 ++ include/asm-arm/arch-pxa/htcuniversal-init.h | 14 include/asm-arm/arch-pxa/htcuniversal.h | 3 include/asm-arm/arch-pxa/irqs.h | 2 include/asm-arm/arch-pxa/pxa-pm_ll.h | 6 include/asm-arm/arch-pxa/pxa-regs.h | 2 include/asm-arm/arch-pxa/serial.h | 78 include/asm-arm/hardware/asic3_keys.h | 18 include/asm-arm/hardware/asic3_leds.h | 34 include/asm-arm/hardware/ipaq-asic3.h | 602 +++++++ include/linux/backlight.h | 7 include/linux/gpiodev.h | 44 include/linux/input_pda.h | 47 include/linux/ioport.h | 1 include/linux/soc/asic3_base.h | 104 + include/linux/soc/tmio_mmc.h | 17 56 files changed, 7469 insertions(+), 1 deletion(-) Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/Makefile =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/Makefile 2008-03-10 16:09:23.000000000 +0000 @@ -0,0 +1,19 @@ +# +# Makefile for HTC Universal +# + +snd-htcuniversal-ak4641-objs := htcuniversal_ak4641.o + +obj-$(CONFIG_MACH_HTCUNIVERSAL) += htcuniversal.o +obj-$(CONFIG_HTCUNIVERSAL_CORE) += htcuniversal_core.o +obj-$(CONFIG_HTCUNIVERSAL_POWER) += htcuniversal_power2.o +obj-$(CONFIG_HTCUNIVERSAL_LCD) += htcuniversal_lcd.o +obj-$(CONFIG_HTCUNIVERSAL_BACKLIGHT) += htcuniversal_bl.o +obj-$(CONFIG_HTCUNIVERSAL_TS2) += htcuniversal_ts2.o +obj-$(CONFIG_HTCUNIVERSAL_BUTTONS) += htcuniversal_buttons.o +obj-$(CONFIG_HTCUNIVERSAL_BLUETOOTH) += htcuniversal_bt.o +obj-$(CONFIG_HTCUNIVERSAL_PHONE) += htcuniversal_phone.o +obj-$(CONFIG_HTCUNIVERSAL_ASIC3_LEDS) += htcuniversal_asic3_leds.o +obj-$(CONFIG_HTCUNIVERSAL_UDC) += htcuniversal_udc.o + +obj-$(CONFIG_HTCUNIVERSAL_AK4641) += htcuniversal_ak4641.o Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal.c 2008-03-10 16:09:23.000000000 +0000 @@ -0,0 +1,468 @@ +/* + * Hardware definitions for HTC Universal + * + * Copyright (c) 2006 Oleg Gusev + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "../generic.h" + +#include "htcuniversal_bt.h" +#include "htcuniversal_phone.h" +#include "tsc2046_ts.h" + +/* + * IRDA + */ + +static void htcuniversal_irda_transceiver_mode(struct device *dev, int mode) +{ + /* */ +} + +static struct pxaficp_platform_data htcuniversal_ficp_platform_data = { + .transceiver_cap = IR_SIRMODE | IR_FIRMODE, + .transceiver_mode = htcuniversal_irda_transceiver_mode, +}; + +/* + * Bluetooth - Relies on other loadable modules, like ASIC3 and Core, + * so make the calls indirectly through pointers. Requires that the + * htcuniversal_bt module be loaded before any attempt to use + * bluetooth (obviously). + */ + +static struct htcuniversal_bt_funcs bt_funcs; + +static void +htcuniversal_bt_configure( int state ) +{ + if (bt_funcs.configure != NULL) + bt_funcs.configure( state ); +} + +static struct htcuniversal_phone_funcs phone_funcs; + +static void +htcuniversal_phone_configure( int state ) +{ + if (phone_funcs.configure != NULL) + phone_funcs.configure( state ); +} + +//void htcuniversal_ll_pm_init(void); + +extern struct platform_device htcuniversal_bl; +static struct platform_device htcuniversal_lcd = { .name = "htcuniversal_lcd", }; +//static struct platform_device htcuniversal_kbd = { .name = "htcuniversal_kbd", }; +static struct platform_device htcuniversal_buttons = { .name = "htcuniversal_buttons", }; +//static struct platform_device htcuniversal_ts = { .name = "htcuniversal_ts", }; +//static struct platform_device htcuniversal_bt = { .name = "htcuniversal_bt", }; +//static struct platform_device htcuniversal_phone = { .name = "htcuniversal_phone", }; +static struct platform_device htcuniversal_power = { .name = "htcuniversal_power", }; +static struct platform_device htcuniversal_udc = { .name = "htcuniversal_udc", }; + +static struct tsc2046_mach_info htcuniversal_ts_platform_data = { + .port = 1, + .clock = CKEN_SSP1, + .pwrbit_X = 1, + .pwrbit_Y = 1, + .irq = 0 /* asic3 irq */ +}; + +static struct platform_device htcuniversal_ts = { + .name = "htcuniversal_ts", + .dev = { + .platform_data = &htcuniversal_ts_platform_data, + }, +}; + + +/* Bluetooth */ + +static struct platform_device htcuniversal_bt = { + .name = "htcuniversal_bt", + .id = -1, + .dev = { + .platform_data = &bt_funcs, + }, +}; + +static struct platform_device htcuniversal_phone = { + .name = "htcuniversal_phone", + .id = -1, + .dev = { + .platform_data = &phone_funcs, + }, +}; + +/* PXA2xx Keys */ + +static struct gpio_keys_button htcuniversal_button_table[] = { + { KEY_POWER, GPIO_NR_HTCUNIVERSAL_KEY_ON_N, 1 }, +}; + +static struct gpio_keys_platform_data htcuniversal_pxa_keys_data = { + .buttons = htcuniversal_button_table, + .nbuttons = ARRAY_SIZE(htcuniversal_button_table), +}; + +static struct platform_device htcuniversal_pxa_keys = { + .name = "gpio-keys", + .dev = { + .platform_data = &htcuniversal_pxa_keys_data, + }, + .id = -1, +}; + +/**************************************************************** + * Keyboard + ****************************************************************/ + +static struct pxa27x_keyboard_platform_data htcuniversal_kbd = { + .nr_rows = 8, + .nr_cols = 8, + .keycodes = { + { + /* row 0 */ + KEY_ENTER, + KEY_MINUS, + KEY_ESC, + KEY_1, + KEY_TAB, + KEY_CAPSLOCK, + KEY_LEFTSHIFT, + KEY_RIGHTALT, /* Fn */ + }, { /* row 1 */ + KEY_COMMA, + KEY_EQUAL, + KEY_F1, + KEY_2, + KEY_Q, + KEY_A, + KEY_Z, + KEY_LEFTCTRL, + }, { /* row 2 */ + KEY_UP, + KEY_I, + KEY_F2, + KEY_3, + KEY_W, + KEY_S, + KEY_X, + KEY_F6, + }, { /* row 3 */ + KEY_DOT, + KEY_O, + KEY_F3, + KEY_4, + KEY_E, + KEY_D, + KEY_C, + KEY_LEFTALT, + }, { /* row 4 */ + KEY_F9, + KEY_P, + KEY_F4, + KEY_5, + KEY_R, + KEY_F, + KEY_V, + KEY_SPACE, + }, { /* row 5 */ + KEY_RIGHT, + KEY_BACKSPACE, + KEY_F5, + KEY_6, + KEY_T, + KEY_G, + KEY_B, + KEY_F7, + }, { /* row 6 */ + KEY_F9, + KEY_K, + KEY_9, + KEY_7, + KEY_Y, + KEY_H, + KEY_N, + KEY_LEFT, + }, { /* row 7 */ + KEY_F10, + KEY_L, + KEY_0, + KEY_8, + KEY_U, + KEY_J, + KEY_M, + KEY_DOWN, + }, + }, + .gpio_modes = { + GPIO_NR_HTCUNIVERSAL_KP_MKIN0_MD, + GPIO_NR_HTCUNIVERSAL_KP_MKIN1_MD, + GPIO_NR_HTCUNIVERSAL_KP_MKIN2_MD, + GPIO_NR_HTCUNIVERSAL_KP_MKIN3_MD, + GPIO_NR_HTCUNIVERSAL_KP_MKIN4_MD, + GPIO_NR_HTCUNIVERSAL_KP_MKIN5_MD, + GPIO_NR_HTCUNIVERSAL_KP_MKIN6_MD, + GPIO_NR_HTCUNIVERSAL_KP_MKIN7_MD, + GPIO_NR_HTCUNIVERSAL_KP_MKOUT0_MD, + GPIO_NR_HTCUNIVERSAL_KP_MKOUT1_MD, + GPIO_NR_HTCUNIVERSAL_KP_MKOUT2_MD, + GPIO_NR_HTCUNIVERSAL_KP_MKOUT3_MD, + GPIO_NR_HTCUNIVERSAL_KP_MKOUT4_MD, + GPIO_NR_HTCUNIVERSAL_KP_MKOUT5_MD, + GPIO_NR_HTCUNIVERSAL_KP_MKOUT6_MD, + GPIO_NR_HTCUNIVERSAL_KP_MKOUT7_MD, + }, +}; + +static struct platform_device htcuniversal_pxa_keyboard = { + .name = "pxa27x-keyboard", + .id = -1, + .dev = { + .platform_data = &htcuniversal_kbd, + }, +}; +/* Core Hardware Functions */ + +struct platform_device htcuniversal_core = { + .name = "htcuniversal_core", + .id = 0, + .dev = { + .platform_data = NULL, + }, +}; + +static struct platform_device *devices[] __initdata = { + &htcuniversal_core, +// &htcuniversal_flash, + &htcuniversal_pxa_keyboard, + &htcuniversal_pxa_keys, +}; + +static struct platform_device *htcuniversal_asic3_devices[] __initdata = { + &htcuniversal_lcd, +#ifdef CONFIG_HTCUNIVERSAL_BACKLIGHT + &htcuniversal_bl, +#endif + &htcuniversal_buttons, + &htcuniversal_ts, + &htcuniversal_bt, + &htcuniversal_phone, + &htcuniversal_power, + &htcuniversal_udc, +}; + +static struct asic3_platform_data htcuniversal_asic3_platform_data = { + + /* Setting ASIC3 GPIO registers to the below initialization states + * HTC Universal asic3 information: + * http://wiki.xda-developers.com/index.php?pagename=UniversalASIC3 + * http://wiki.xda-developers.com/index.php?pagename=ASIC3 + * + * dir: Direction of the GPIO pin. 0: input, 1: output. + * If unknown, set as output to avoid power consuming floating input nodes + * init: Initial state of the GPIO bits + * + * These registers are configured as they are on Wince. + */ + .gpio_a = { + .dir = (1<> 18) & 0xfffc, + .boot_params = 0xa0000100, + .map_io = htcuniversal_map_io, + .init_irq = htcuniversal_init_irq, + .init_machine = htcuniversal_init, + .timer = &pxa_timer, +MACHINE_END Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.c 2008-03-10 16:09:23.000000000 +0000 @@ -0,0 +1,917 @@ +/* + * Audio support for codec Asahi Kasei AK4641 + * + * 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) 2006 Giorgio Padrin + * + * History: + * + * 2006-03 Written -- Giorgio Padrin + * 2006-09 Test and debug on machine (HP hx4700) -- Elshin Roman + * + * AK4641 codec device driver + * + * Copyright (c) 2005 SDG Systems, LLC + * + * Based on code: + * Copyright (c) 2002 Hewlett-Packard Company + * Copyright (c) 2000 Nicolas Pitre + * Copyright (c) 2000 Lernout & Hauspie Speech Products, N.V. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "htcuniversal_ak4641.h" + +/* Registers */ +#define R_PM1 0x00 +#define R_PM2 0x01 +#define R_SEL1 0x02 +#define R_SEL2 0x03 +#define R_MODE1 0x04 +#define R_MODE2 0x05 +#define R_DAC 0x06 +#define R_MIC 0x07 +#define REG_TIMER 0x08 +#define REG_ALC1 0x09 +#define REG_ALC2 0x0a +#define R_PGA 0x0b +#define R_ATTL 0x0c +#define R_ATTR 0x0d +#define REG_VOL 0x0e +#define R_STATUS 0x0f +#define REG_EQLO 0x10 +#define REG_EQMID 0x11 +#define REG_EQHI 0x12 +#define REG_BTIF 0x13 + +/* Register flags */ +/* REG_PWR1 */ +#define R_PM1_PMADC 0x01 +#define R_PM1_PMMIC 0x02 +#define REG_PWR1_PMAUX 0x04 +#define REG_PWR1_PMMO 0x08 +#define R_PM1_PMLO 0x10 +/* unused 0x20 */ +/* unused 0x40 */ +#define R_PM1_PMVCM 0x80 + +/* REG_PWR2 */ +#define R_PM2_PMDAC 0x01 +/* unused 0x02 */ +/* unused 0x04 */ +#define R_PM2_PMMO2 0x08 +#define REG_PWR2_MCKAC 0x10 +/* unused 0x20 */ +/* unused 0x40 */ +#define R_PM2_MCKPD 0x80 + +/* REG_SEL1 */ +#define R_SEL1_PSMO2 0x01 +/* unused 0x02 */ +/* unused 0x04 */ +/* unused 0x08 */ +#define REG_SEL1_MICM 0x10 +#define REG_SEL1_DACM 0x20 +#define REG_SEL1_PSMO 0x40 +#define REG_SEL1_MOGN 0x80 + +/* REG_SEL2 */ +#define R_SEL2_PSLOR 0x01 +#define R_SEL2_PSLOL 0x02 +#define REG_SEL2_AUXSI 0x04 +/* unused 0x08 */ +#define REG_SEL2_MICL 0x10 +#define REG_SEL2_AUXL 0x20 +/* unused 0x40 */ +#define R_SEL2_DACL 0x80 + +/* REG_MODE1 */ +#define REG_MODE1_DIF0 0x01 +#define REG_MODE1_DIF1 0x02 +/* unused 0x04 */ +/* unused 0x08 */ +/* unused 0x10 */ +/* unused 0x20 */ +/* unused 0x40 */ +/* unused 0x80 */ + +/* REG_MODE2 */ +/* unused 0x01 */ +#define REG_MODE2_LOOP 0x02 +#define REG_MODE2_HPM 0x04 +/* unused 0x08 */ +/* unused 0x10 */ +#define REG_MODE2_MCK0 0x20 +#define REG_MODE2_MCK1 0x40 +/* unused 0x80 */ + +/* REG_DAC */ +#define REG_DAC_DEM0 0x01 +#define REG_DAC_DEM1 0x02 +#define REG_DAC_EQ 0x04 +/* unused 0x08 */ +#define R_DAC_DATTC 0x10 +#define R_DAC_SMUTE 0x20 +#define REG_DAC_TM 0x40 +/* unused 0x80 */ + +/* REG_MIC */ +#define R_MIC_MGAIN 0x01 +#define R_MIC_MSEL 0x02 +#define R_MIC_MICAD 0x04 +#define R_MIC_MPWRI 0x08 +#define R_MIC_MPWRE 0x10 +#define REG_MIC_AUXAD 0x20 +/* unused 0x40 */ +/* unused 0x80 */ + +/* REG_TIMER */ + +#define REG_TIMER_LTM0 0x01 +#define REG_TIMER_LTM1 0x02 +#define REG_TIMER_WTM0 0x04 +#define REG_TIMER_WTM1 0x08 +#define REG_TIMER_ZTM0 0x10 +#define REG_TIMER_ZTM1 0x20 +/* unused 0x40 */ +/* unused 0x80 */ + +#define REG_ALC1_LMTH 0x01 +#define REG_ALC1_RATT 0x02 +#define REG_ALC1_LMAT0 0x04 +#define REG_ALC1_LMAT1 0x08 +#define REG_ALC1_ZELM 0x10 +#define REG_ALC1_ALC1 0x20 +/* unused 0x40 */ +/* unused 0x80 */ + +/* REG_ALC2 */ + +/* REG_PGA */ + +/* REG_ATTL */ + +/* REG_ATTR */ + +/* REG_VOL */ +#define REG_VOL_ATTM 0x80 + +/* REG_STATUS */ +#define R_STATUS_DTMIC 0x01 + +/* REG_EQ controls use 4 bits for each of 5 EQ levels */ + +/* Bluetooth not yet implemented */ +#define REG_BTIF_PMAD2 0x01 +#define REG_BTIF_PMDA2 0x02 +#define REG_BTIF_PMBIF 0x04 +#define REG_BTIF_ADC2 0x08 +#define REG_BTIF_DAC2 0x10 +#define REG_BTIF_BTFMT0 0x20 +#define REG_BTIF_BTFMT1 0x40 +/* unused 0x80 */ + +/* begin {{ I2C }} */ + +static struct i2c_driver snd_ak4641_i2c_driver = { + .driver = { + .name = "ak4641-i2c" + }, +}; + +static int snd_ak4641_i2c_init(void) +{ + return i2c_add_driver(&snd_ak4641_i2c_driver); +} + +static void snd_ak4641_i2c_free(void) +{ + i2c_del_driver(&snd_ak4641_i2c_driver); +} + +static inline int snd_ak4641_i2c_probe(struct snd_ak4641 *ak) +{ + if (ak->i2c_client.adapter == NULL) return -EINVAL; + ak->i2c_client.addr = 0x12; + if (i2c_smbus_xfer(ak->i2c_client.adapter, ak->i2c_client.addr, + 0, 0, 0, I2C_SMBUS_QUICK, NULL) < 0) + return -ENODEV; + else return 0; +} + +static int snd_ak4641_i2c_attach(struct snd_ak4641 *ak) +{ + int ret = 0; + if ((ret = snd_ak4641_i2c_probe(ak)) < 0) return ret; + snprintf(ak->i2c_client.name, sizeof(ak->i2c_client.name), + "ak4641-i2c at %d-%04x", + i2c_adapter_id(ak->i2c_client.adapter), ak->i2c_client.addr); + return i2c_attach_client(&ak->i2c_client); +} + +static void snd_ak4641_i2c_detach(struct snd_ak4641 *ak) +{ + i2c_detach_client(&ak->i2c_client); +} + +/* end {{ I2C }} */ + + +/* begin {{ Registers & Cache Ops }} */ + +static int snd_ak4641_hwsync(struct snd_ak4641 *ak, int read, u8 reg) +{ + struct i2c_msg msgs[2]; + u8 buf[2]; + int ret; + + snd_assert(reg < ARRAY_SIZE(ak->regs), return -EINVAL); + + /* setup i2c msgs */ + msgs[0].addr = ak->i2c_client.addr; + msgs[0].flags = 0; + msgs[0].buf = buf; + if (!read) + msgs[0].len = 2; + 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 = 1; + } + + buf[0] = reg; + + /* regs[reg] -> buffer, on write */ + if (!read) buf[1] = ak->regs[reg]; + + /* i2c transfer */ + ret = i2c_transfer(ak->i2c_client.adapter, msgs, read ? 2 : 1); + if (ret != (read ? 2 : 1)) return ret; /* transfer error */ //@@ error ret < 0, or not ? + + /* regs[reg] <- buffer, on read */ + if (read) ak->regs[reg] = buf[1]; + + return 0; +} + +static inline int snd_ak4641_hwsync_read(struct snd_ak4641 *ak, u8 reg) +{ + return snd_ak4641_hwsync(ak, 1, reg); +} + +static inline int snd_ak4641_hwsync_write(struct snd_ak4641 *ak, u8 reg) +{ + return snd_ak4641_hwsync(ak, 0, reg); +} + +static int snd_ak4641_hwsync_read_all(struct snd_ak4641 *ak) +{ + u8 reg; + for (reg = 0; reg < ARRAY_SIZE(ak->regs); reg++) + if (snd_ak4641_hwsync_read(ak, reg) < 0) return -1; + return 0; +} + +static int snd_ak4641_hwsync_write_all(struct snd_ak4641 *ak) +{ + u8 reg; + for (reg = 0; reg < ARRAY_SIZE(ak->regs); reg++) + if (snd_ak4641_hwsync_write(ak, reg) < 0) return -1; + return 0; +} + +static int snd_ak4641_reg_changed(struct snd_ak4641 *ak, u8 reg) +{ + if ((reg != R_PGA && ak->powered_on) || + (reg == R_PGA && (ak->regs[R_PM1] & R_PM1_PMMIC))) + return snd_ak4641_hwsync_write(ak, reg); + return 0; +} + +/* end {{ Registers & Cache Ops }}*/ + + +static inline void snd_ak4641_lock(struct snd_ak4641 *ak) +{ + down(&ak->sem); +} + +static inline void snd_ak4641_unlock(struct snd_ak4641 *ak) +{ + up(&ak->sem); +} + +#define WRITE_MASK(i, val, mask) (((i) & ~(mask)) | ((val) & (mask))) + + +/* begin {{ Controls }} */ + +#define INV_RANGE(val, mask) \ + (~(val) & (mask)) + +/*-begin----------------------------------------------------------*/ +static int snd_ak4641_actl_playback_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xff; + return 0; +} + +static int snd_ak4641_actl_playback_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data; + + snd_ak4641_lock(ak); + ucontrol->value.integer.value[0] = INV_RANGE(ak->regs[R_ATTL], 0xff); + ucontrol->value.integer.value[1] = INV_RANGE(ak->regs[R_ATTR], 0xff); + snd_ak4641_unlock(ak); + return 0; +} + +static int snd_ak4641_actl_playback_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data; + + snd_ak4641_lock(ak); + ak->regs[R_ATTL] = INV_RANGE(ucontrol->value.integer.value[0], 0xff); + ak->regs[R_ATTR] = INV_RANGE(ucontrol->value.integer.value[1], 0xff); + snd_ak4641_reg_changed(ak, R_ATTL); + snd_ak4641_reg_changed(ak, R_ATTR); + snd_ak4641_unlock(ak); + return 0; +} +/*-end------------------------------------------------------------*/ + +/*-begin----------------------------------------------------------*/ +static int snd_ak4641_actl_mic_gain_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0x7f; + return 0; +} + +static int snd_ak4641_actl_mic_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data; + + ucontrol->value.integer.value[0] = ak->regs[R_PGA]; + return 0; +} + +static int snd_ak4641_actl_mic_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data; + + snd_ak4641_lock(ak); + ak->regs[R_PGA] = ucontrol->value.integer.value[0]; + snd_ak4641_reg_changed(ak, R_PGA); + snd_ak4641_unlock(ak); + return 0; +} +/*-end------------------------------------------------------------*/ + +#define ACTL(ctl_name, _name) \ +static struct snd_kcontrol_new snd_ak4641_actl_ ## ctl_name = \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = _name, \ + .info = snd_ak4641_actl_ ## ctl_name ## _info, \ + .get = snd_ak4641_actl_ ## ctl_name ## _get, .put = snd_ak4641_actl_ ## ctl_name ## _put }; + +ACTL(playback_volume, "Master Playback Volume") +ACTL(mic_gain, "Mic Capture Gain") + +struct snd_ak4641_uctl_bool { + int (*get) (struct snd_ak4641 *uda); + int (*set) (struct snd_ak4641 *uda, int on); +}; + +static int snd_ak4641_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_ak4641_actl_bool_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data; + struct snd_ak4641_uctl_bool *uctl = + (struct snd_ak4641_uctl_bool *) kcontrol->private_value; + + ucontrol->value.integer.value[0] = uctl->get(ak); + return 0; +} + +static int snd_ak4641_actl_bool_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data; + struct snd_ak4641_uctl_bool *uctl = + (struct snd_ak4641_uctl_bool *) kcontrol->private_value; + + return uctl->set(ak, ucontrol->value.integer.value[0]); +} + +/*-begin----------------------------------------------------------*/ +static int snd_ak4641_uctl_playback_switch_get(struct snd_ak4641 *ak) +{ + return (ak->regs[R_DAC] & R_DAC_SMUTE) == 0x00; +} + +static int snd_ak4641_uctl_playback_switch_set(struct snd_ak4641 *ak, int on) +{ + snd_ak4641_lock(ak); + ak->regs[R_DAC] = WRITE_MASK(ak->regs[R_DAC], + on ? 0x00 : R_DAC_SMUTE, R_DAC_SMUTE); + snd_ak4641_reg_changed(ak, R_DAC); + snd_ak4641_unlock(ak); + return 0; +} +/*-end------------------------------------------------------------*/ + +/*-begin----------------------------------------------------------*/ +static int snd_ak4641_uctl_mic_boost_get(struct snd_ak4641 *ak) +{ + return (ak->regs[R_MIC] & R_MIC_MGAIN) == R_MIC_MGAIN; +} + +static int snd_ak4641_uctl_mic_boost_set(struct snd_ak4641 *ak, int on) +{ + snd_ak4641_lock(ak); + ak->regs[R_MIC] = WRITE_MASK(ak->regs[R_MIC], + on ? R_MIC_MGAIN : 0x00, R_MIC_MGAIN); + snd_ak4641_reg_changed(ak, R_MIC); + snd_ak4641_unlock(ak); + return 0; +} +/*-end------------------------------------------------------------*/ + +/*-begin----------------------------------------------------------*/ +static int snd_ak4641_uctl_mono_out_get(struct snd_ak4641 *ak) +{ + printk("mono_out status 0x%8.8x -> 0x%8.8x\n",ak->regs[R_SEL1], ak->regs[R_SEL1] & REG_SEL1_PSMO); + return (ak->regs[R_SEL1] & REG_SEL1_PSMO) == REG_SEL1_PSMO; +} + +static int snd_ak4641_uctl_mono_out_set(struct snd_ak4641 *ak, int on) +{ + printk("phone mic enable called. on=%d\n",on); + snd_ak4641_lock(ak); + ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], on ? R_PM1_PMMIC : 0x00, R_PM1_PMMIC); + ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], on ? REG_PWR1_PMMO : 0x00, REG_PWR1_PMMO); + snd_ak4641_reg_changed(ak, R_PM1); + + snd_ak4641_hwsync_write(ak, R_PGA); /* mic PGA gain is reset when PMMIC = 0 */ + + /* internal mic */ + ak->regs[R_MIC] = WRITE_MASK(ak->regs[R_MIC], on ? R_MIC_MPWRI : 0x0, R_MIC_MPWRI); + ak->regs[R_MIC] = WRITE_MASK(ak->regs[R_MIC], 0x0, R_MIC_MSEL); + snd_ak4641_hwsync_write(ak, R_MIC); + +// ak->regs[REG_BTIF] = WRITE_MASK(ak->regs[REG_BTIF], 0x0, REG_BTIF_DAC2); +// snd_ak4641_hwsync_write(ak, REG_BTIF); + /* */ +// ak->regs[REG_VOL] = WRITE_MASK(ak->regs[REG_VOL], on ? REG_VOL_ATTM : 0x00, REG_VOL_ATTM); +// ak->regs[R_SEL1] = WRITE_MASK(ak->regs[R_SEL1], on ? REG_SEL1_MOGN : 0x00, REG_SEL1_MOGN); + ak->regs[R_SEL1] = WRITE_MASK(ak->regs[R_SEL1], on ? REG_SEL1_MICM : 0x00, REG_SEL1_MICM); + ak->regs[R_SEL1] = WRITE_MASK(ak->regs[R_SEL1], on ? REG_SEL1_PSMO : 0x00, REG_SEL1_PSMO); + snd_ak4641_reg_changed(ak, R_SEL1); + snd_ak4641_unlock(ak); + return 0; +} +/*-end------------------------------------------------------------*/ + +#define ACTL_BOOL(ctl_name, _name) \ +static struct snd_ak4641_uctl_bool snd_ak4641_actl_ ## ctl_name ## _pvalue = \ +{ .get = snd_ak4641_uctl_ ## ctl_name ## _get, \ + .set = snd_ak4641_uctl_ ## ctl_name ## _set }; \ +static struct snd_kcontrol_new snd_ak4641_actl_ ## ctl_name = \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = _name, .info = snd_ak4641_actl_bool_info, \ + .get = snd_ak4641_actl_bool_get, .put = snd_ak4641_actl_bool_put, \ + .private_value = (unsigned long) &snd_ak4641_actl_ ## ctl_name ## _pvalue }; + +ACTL_BOOL(playback_switch, "Master Playback Switch") +ACTL_BOOL(mic_boost, "Mic Boost (+20dB)") +ACTL_BOOL(mono_out, "Phone mic enable") + +static void snd_ak4641_headphone_on(struct snd_ak4641 *ak, int on); +static void snd_ak4641_speaker_on(struct snd_ak4641 *ak, int on); +static void snd_ak4641_select_mic(struct snd_ak4641 *ak); + +void snd_ak4641_hp_connected(struct snd_ak4641 *ak, int connected) +{ + snd_ak4641_lock(ak); + if (connected != ak->hp_connected) { + ak->hp_connected = connected; + + /* headphone or speaker, on playback */ + if (ak->playback_on) { + if (connected) { + snd_ak4641_headphone_on(ak, 1); + snd_ak4641_speaker_on(ak, 0); + } else { + snd_ak4641_speaker_on(ak, 1); + snd_ak4641_headphone_on(ak, 0); + } + } + + /* headset or internal mic, on capture */ + if (ak->capture_on) + snd_ak4641_select_mic(ak); + } + snd_ak4641_unlock(ak); +} + +/* end {{ Controls }} */ + + +/* begin {{ Headphone Detected Notification }} */ + +static void snd_ak4641_hp_detected_w_fn(void *p) +{ + struct snd_ak4641 *ak = (struct snd_ak4641 *)p; + + snd_ak4641_hp_connected(ak, ak->hp_detected.detected); +} + +void snd_ak4641_hp_detected(struct snd_ak4641 *ak, int detected) +{ + if (detected != ak->hp_detected.detected) { + ak->hp_detected.detected = detected; + queue_work(ak->hp_detected.wq, &ak->hp_detected.w); + } +} + +static int snd_ak4641_hp_detected_init(struct snd_ak4641 *ak) +{ + INIT_WORK(&ak->hp_detected.w, snd_ak4641_hp_detected_w_fn); + ak->hp_detected.detected = ak->hp_connected; + ak->hp_detected.wq = create_singlethread_workqueue("ak4641"); + if (ak->hp_detected.wq) return 0; + else return -1; +} + +static void snd_ak4641_hp_detected_free(struct snd_ak4641 *ak) +{ + destroy_workqueue(ak->hp_detected.wq); +} + +/* end {{ Headphone Detected Notification }} */ + + +/* begin {{ Codec Control }} */ + +static void snd_ak4641_headphone_on(struct snd_ak4641 *ak, int on) +{ + if (on) { + ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], R_PM1_PMLO, R_PM1_PMLO); + snd_ak4641_hwsync_write(ak, R_PM1); + ak->headphone_out_on(1); + ak->regs[R_SEL2] = WRITE_MASK(ak->regs[R_SEL2], + R_SEL2_PSLOL | R_SEL2_PSLOR, + R_SEL2_PSLOL | R_SEL2_PSLOR); + snd_ak4641_hwsync_write(ak, R_SEL2); + } else { + ak->regs[R_SEL2] = WRITE_MASK(ak->regs[R_SEL2], + 0x00, R_SEL2_PSLOL | R_SEL2_PSLOR); + snd_ak4641_hwsync_write(ak, R_SEL2); + ak->headphone_out_on(0); + ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], 0x00, R_PM1_PMLO); + snd_ak4641_hwsync_write(ak, R_PM1); + } +} + +static void snd_ak4641_speaker_on(struct snd_ak4641 *ak, int on) +{ + if (on) { + ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], R_PM1_PMLO, R_PM1_PMLO); + snd_ak4641_hwsync_write(ak, R_PM1); + ak->speaker_out_on(1); + ak->regs[R_SEL2] = WRITE_MASK(ak->regs[R_SEL2], + R_SEL2_PSLOL | R_SEL2_PSLOR, + R_SEL2_PSLOL | R_SEL2_PSLOR); + snd_ak4641_hwsync_write(ak, R_SEL2); + } else { + ak->regs[R_SEL2] = WRITE_MASK(ak->regs[R_SEL2], + 0x00, R_SEL2_PSLOL | R_SEL2_PSLOR); + snd_ak4641_hwsync_write(ak, R_SEL2); + ak->speaker_out_on(0); + ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], 0x00, R_PM1_PMLO); + snd_ak4641_hwsync_write(ak, R_PM1); + } +} + +static inline int snd_ak4641_power_on(struct snd_ak4641 *ak) +{ + ak->reset_pin(1); + ak->power_on_chip(1); + msleep(1); + ak->reset_pin(0); + ak->powered_on = 1; + return 0; +} + +static inline int snd_ak4641_power_off(struct snd_ak4641 *ak) +{ + ak->powered_on = 0; + ak->power_on_chip(0); + return 0; +} + +static inline void snd_ak4641_headphone_out_on(struct snd_ak4641 *ak, int on) +{ + if (ak->headphone_out_on) ak->headphone_out_on(on); +} + +static inline void snd_ak4641_speaker_out_on(struct snd_ak4641 *ak, int on) +{ + if (ak->speaker_out_on) ak->speaker_out_on(on); +} + +static int snd_ak4641_playback_on(struct snd_ak4641 *ak) +{ + if (ak->playback_on) return 0; + + ak->regs[R_PM2] = WRITE_MASK(ak->regs[R_PM2], + R_PM2_PMDAC, R_PM2_MCKPD | R_PM2_PMDAC); + ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], R_PM1_PMLO, R_PM1_PMLO); + snd_ak4641_hwsync_write(ak, R_PM2); + snd_ak4641_hwsync_write(ak, R_PM1); + if (ak->hp_connected) snd_ak4641_headphone_on(ak, 1); + else snd_ak4641_speaker_on(ak, 1); + + ak->playback_on = 1; + + return 0; +} + +static int snd_ak4641_playback_off(struct snd_ak4641 *ak) +{ + if (!ak->playback_on) return 0; + + ak->playback_on = 0; + + if (ak->hp_connected) snd_ak4641_headphone_on(ak, 0); + else snd_ak4641_speaker_on(ak, 0); + ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], 0x00, R_PM1_PMLO); + ak->regs[R_PM2] = WRITE_MASK(ak->regs[R_PM2], + (!ak->capture_on ? R_PM2_MCKPD : 0x00) | R_PM2_PMDAC, + R_PM2_MCKPD | R_PM2_PMDAC); + snd_ak4641_hwsync_write(ak, R_PM1); + snd_ak4641_hwsync_write(ak, R_PM2); + + return 0; +} + +static void snd_ak4641_select_mic(struct snd_ak4641 *ak) +{ + int mic = 0; + u8 r_mic; + + if (ak->hp_connected) { + /* check headset mic */ + ak->regs[R_MIC] = WRITE_MASK(ak->regs[R_MIC], R_MIC_MPWRE, R_MIC_MPWRE); + snd_ak4641_hwsync_write(ak, R_MIC); + snd_ak4641_hwsync_read(ak, R_STATUS); + mic = (ak->regs[R_STATUS] & R_STATUS_DTMIC) == R_STATUS_DTMIC; + + printk("htcuniversal_ak4641_select_mic: mic=%d\n",mic); + + r_mic = WRITE_MASK(ak->regs[R_MIC], + R_MIC_MSEL | (ak->capture_on ? R_MIC_MPWRE : 0x00), + R_MIC_MSEL | R_MIC_MPWRI | R_MIC_MPWRE); + } + else + r_mic = WRITE_MASK(ak->regs[R_MIC], + 0x00 | (ak->capture_on ? R_MIC_MPWRI : 0x00), + R_MIC_MSEL | R_MIC_MPWRI | R_MIC_MPWRE); + + if (r_mic != ak->regs[R_MIC]) { + ak->regs[R_MIC] = r_mic; + snd_ak4641_hwsync_write(ak, R_MIC); + } +} + +static int snd_ak4641_capture_on(struct snd_ak4641 *ak) +{ + if (ak->capture_on) return 0; + + if (!ak->playback_on) { + ak->regs[R_PM2] = WRITE_MASK(ak->regs[R_PM2], 0x00, R_PM2_MCKPD); + snd_ak4641_hwsync_write(ak, R_PM2); + } + ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], R_PM1_PMMIC | R_PM1_PMADC, + R_PM1_PMMIC | R_PM1_PMADC); + snd_ak4641_hwsync_write(ak, R_PM1); + snd_ak4641_hwsync_write(ak, R_PGA); /* mic PGA gain is reset when PMMIC = 0 */ + + ak->capture_on = 1; + + snd_ak4641_select_mic(ak); + + msleep(47); /* accounts for ADC init cycle, time enough for fs >= 44.1 kHz */ + + return 0; +} + +static int snd_ak4641_capture_off(struct snd_ak4641 *ak) +{ + if (!ak->capture_on) return 0; + + ak->regs[R_MIC] = WRITE_MASK(ak->regs[R_MIC], + 0x00, R_MIC_MPWRI | R_MIC_MPWRE | R_MIC_MSEL); + ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], 0x00, R_PM1_PMMIC | R_PM1_PMADC); + snd_ak4641_hwsync_write(ak, R_MIC); + snd_ak4641_hwsync_write(ak, R_PM1); + if (!ak->playback_on) { + ak->regs[R_PM2] = WRITE_MASK(ak->regs[R_PM2], R_PM2_MCKPD, R_PM2_MCKPD); + snd_ak4641_hwsync_write(ak, R_PM2); + } + + ak->capture_on = 0; + + return 0; +} + +int snd_ak4641_open_stream(struct snd_ak4641 *ak, int stream) +{ + snd_ak4641_lock(ak); + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + ak->playback_stream_opened = 1; + snd_ak4641_playback_on(ak); + } else { + ak->capture_stream_opened = 1; + snd_ak4641_capture_on(ak); + } + snd_ak4641_unlock(ak); + return 0; +} + +int snd_ak4641_close_stream(struct snd_ak4641 *ak, int stream) +{ + snd_ak4641_lock(ak); + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + ak->playback_stream_opened = 0; + snd_ak4641_playback_off(ak); + } else { + ak->capture_stream_opened = 0; + snd_ak4641_capture_off(ak); + } + snd_ak4641_unlock(ak); + return 0; +} + +static int snd_ak4641_init_regs(struct snd_ak4641 *ak) +{ + snd_ak4641_hwsync_read_all(ak); + + //@@ MEMO: add some configs + + ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], R_PM1_PMVCM, R_PM1_PMVCM); + ak->regs[R_DAC] = WRITE_MASK(ak->regs[R_DAC], 0x00, R_DAC_DATTC); + snd_ak4641_hwsync_write(ak, R_PM1); + snd_ak4641_hwsync_write(ak, R_DAC); + + return 0; +} + +int snd_ak4641_suspend(struct snd_ak4641 *ak, pm_message_t state) +{ + snd_ak4641_lock(ak); + if (ak->playback_on) snd_ak4641_playback_off(ak); + if (ak->capture_on) snd_ak4641_capture_off(ak); + snd_ak4641_power_off(ak); + snd_ak4641_unlock(ak); + return 0; +} + +int snd_ak4641_resume(struct snd_ak4641 *ak) +{ + snd_ak4641_lock(ak); + snd_ak4641_power_on(ak); + snd_ak4641_hwsync_write_all(ak); + if (ak->playback_stream_opened) snd_ak4641_playback_on(ak); + if (ak->capture_stream_opened) snd_ak4641_capture_on(ak); + snd_ak4641_unlock(ak); + return 0; +} + +static void snd_ak4641_init_ak(struct snd_ak4641 *ak) +{ + init_MUTEX(&ak->sem); + ak->i2c_client.driver = &snd_ak4641_i2c_driver; +} + +int snd_ak4641_activate(struct snd_ak4641 *ak) +{ + int ret = 0; + + snd_ak4641_init_ak(ak); + snd_ak4641_lock(ak); + snd_ak4641_power_on(ak); + if ((ret = snd_ak4641_i2c_attach(ak)) < 0) + goto failed_i2c_attach; + snd_ak4641_init_regs(ak); + if ((ret = snd_ak4641_hp_detected_init(ak)) < 0) + goto failed_hp_detected_init; + snd_ak4641_unlock(ak); + return 0; + + failed_hp_detected_init: + snd_ak4641_i2c_detach(ak); + failed_i2c_attach: + snd_ak4641_power_off(ak); + snd_ak4641_unlock(ak); + return ret; +} + +void snd_ak4641_deactivate(struct snd_ak4641 *ak) +{ + snd_ak4641_lock(ak); + snd_ak4641_hp_detected_free(ak); + snd_ak4641_i2c_detach(ak); + snd_ak4641_power_off(ak); + snd_ak4641_unlock(ak); +} + +int snd_ak4641_add_mixer_controls(struct snd_ak4641 *ak, struct snd_card *card) +{ + snd_ak4641_lock(ak); + snd_ctl_add(card, snd_ctl_new1(&snd_ak4641_actl_playback_volume, ak)); + snd_ctl_add(card, snd_ctl_new1(&snd_ak4641_actl_playback_switch, ak)); + snd_ctl_add(card, snd_ctl_new1(&snd_ak4641_actl_mic_gain, ak)); + snd_ctl_add(card, snd_ctl_new1(&snd_ak4641_actl_mic_boost, ak)); + snd_ctl_add(card, snd_ctl_new1(&snd_ak4641_actl_mono_out, ak)); + snd_ak4641_unlock(ak); + return 0; +} + +/* end {{ Codec Control }} */ + + +/* begin {{ Module }} */ + +static int __init snd_ak4641_module_on_load(void) +{ + snd_ak4641_i2c_init(); + return 0; +} + +static void __exit snd_ak4641_module_on_unload(void) +{ + snd_ak4641_i2c_free(); +} + +module_init(snd_ak4641_module_on_load); +module_exit(snd_ak4641_module_on_unload); + +EXPORT_SYMBOL(snd_ak4641_activate); +EXPORT_SYMBOL(snd_ak4641_deactivate); +EXPORT_SYMBOL(snd_ak4641_add_mixer_controls); +EXPORT_SYMBOL(snd_ak4641_open_stream); +EXPORT_SYMBOL(snd_ak4641_close_stream); +EXPORT_SYMBOL(snd_ak4641_suspend); +EXPORT_SYMBOL(snd_ak4641_resume); +EXPORT_SYMBOL(snd_ak4641_hp_connected); +EXPORT_SYMBOL(snd_ak4641_hp_detected); + +MODULE_AUTHOR("Giorgio Padrin"); +MODULE_DESCRIPTION("Audio support for codec Asahi Kasei AK4641"); +MODULE_LICENSE("GPL"); + +/* end {{ Module }} */ Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.h 2008-03-10 16:09:23.000000000 +0000 @@ -0,0 +1,65 @@ +/* + * Audio support for codec Asahi Kasei AK4641 + * + * 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) 2006 Giorgio Padrin + */ + +#ifndef __SOUND_AK4641_H +#define __SOUND_AK4641_H + +#include + +struct snd_ak4641 { + struct semaphore sem; + + u8 regs[0x14]; /* registers cache */ + + unsigned int + powered_on:1, + playback_on:1, + playback_stream_opened:1, + capture_on:1, + capture_stream_opened:1; + + unsigned int + hp_connected:1; + + /* -- configuration (to fill before activation) -- */ + void (*power_on_chip)(int on); + void (*reset_pin)(int on); + void (*headphone_out_on)(int on); + void (*speaker_out_on)(int on); + + struct i2c_client i2c_client; /* to fill .adapter */ + /* ----------------------------------------------- */ + + struct { + int detected; + struct workqueue_struct *wq; + struct work_struct w; + } hp_detected; +}; + + +/* Note: opening, closing, suspending and resuming a stream + * require the clocks (MCLK and I2S ones) running + */ + +/* don't forget to specify I2C adapter in i2c_client field */ +int snd_ak4641_activate(struct snd_ak4641 *ak); + +void snd_ak4641_deactivate(struct snd_ak4641 *ak); +int snd_ak4641_add_mixer_controls(struct snd_ak4641 *ak, struct snd_card *card); +int snd_ak4641_open_stream(struct snd_ak4641 *ak, int stream); +int snd_ak4641_close_stream(struct snd_ak4641 *ak, int stream); +int snd_ak4641_suspend(struct snd_ak4641 *ak, pm_message_t state); +int snd_ak4641_resume(struct snd_ak4641 *ak); + +void snd_ak4641_hp_connected(struct snd_ak4641 *ak, int connected); /* non atomic context */ +void snd_ak4641_hp_detected(struct snd_ak4641 *ak, int detected); /* atomic context */ + +#endif /* __SOUND_AK4641_H */ Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_asic3_leds.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_asic3_leds.c 2008-03-10 16:09:23.000000000 +0000 @@ -0,0 +1,143 @@ +/* + * LEDs support for the HP iPaq hx4700 + * + * Copyright (c) 2006 Anton Vorontsov + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +//FIXME +//DEFINE_LED_TRIGGER_SHARED_GLOBAL(htcuniversal_radio_trig); +//EXPORT_LED_TRIGGER_SHARED(htcuniversal_radio_trig); + +static struct asic3_led htcuniversal_leds[] = { + { + .led_cdev = { + .name = "htcuniversal:red", + .default_trigger = "htcuniversal-charging", + }, + .hw_num = 2, + + }, + { + .led_cdev = { + .name = "htcuniversal:green", + .default_trigger = "htcuniversal-chargefull", + }, + .hw_num = 1, + }, + { + .led_cdev = { + .name = "htcuniversal:wifi-bt", + .default_trigger = "htcuniversal-radio", + }, + .hw_num = 0, + }, + { + .led_cdev = { + .name = "htcuniversal:phonebuttons", + .default_trigger = "htcuniversal-phonebuttons", + }, + .hw_num = -1, + .gpio_num = ('D'-'A')*16+GPIOD_BL_KEYP_PWR_ON, + }, + { + .led_cdev = { + .name = "htcuniversal:vibra", + .default_trigger = "htcuniversal-vibra", + }, + .hw_num = -1, + .gpio_num = ('D'-'A')*16+GPIOD_VIBRA_PWR_ON, + }, + { + .led_cdev = { + .name = "htcuniversal:flashlight1", + .default_trigger = "htcuniversal-flashlight1", + }, + .hw_num = -1, + .gpio_num = ('A'-'A')*16+GPIOA_FLASHLIGHT, + }, + { + .led_cdev = { + .name = "htcuniversal:kbdbacklight", + .default_trigger = "htcuniversal-kbdbacklight", + }, + .hw_num = -1, + .gpio_num = ('D'-'A')*16+GPIOD_BL_KEYB_PWR_ON, + }, +}; + +void htcuniversal_leds_release(struct device *dev) +{ + return; +} + +static +struct asic3_leds_machinfo htcuniversal_leds_machinfo = { + .num_leds = ARRAY_SIZE(htcuniversal_leds), + .leds = htcuniversal_leds, + .asic3_pdev = &htcuniversal_asic3, +}; + +static +struct platform_device htcuniversal_leds_pdev = { + .name = "asic3-leds", + .dev = { + .platform_data = &htcuniversal_leds_machinfo, + .release = htcuniversal_leds_release, + }, +}; + +static +int __init htcuniversal_leds_init(void) +{ + int ret; + printk("htcuniversal LEDs Driver\n"); +// led_trigger_register_shared("htcuniversal-radio", &htcuniversal_radio_trig); + + ret = asic3_leds_register(); + if (ret) goto asic3_leds_failed; + + ret = platform_device_register(&htcuniversal_leds_pdev); + if (ret) goto platform_device_failed; + + goto success; + +platform_device_failed: + asic3_leds_unregister(); +asic3_leds_failed: +// led_trigger_unregister_shared(htcuniversal_radio_trig); + printk("htcuniversal LEDs Driver failed to init"); +success: + return ret; +} + +static +void __exit htcuniversal_leds_exit(void) +{ +// led_trigger_unregister_shared(htcuniversal_radio_trig); + platform_device_unregister(&htcuniversal_leds_pdev); + asic3_leds_unregister(); + return; +} + +module_init(htcuniversal_leds_init); +module_exit(htcuniversal_leds_exit); + +MODULE_AUTHOR("Anton Vorontsov "); +MODULE_DESCRIPTION("htcuniversal LEDs driver"); +MODULE_LICENSE("GPL"); Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_bl.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_bl.c 2008-03-10 16:09:23.000000000 +0000 @@ -0,0 +1,61 @@ +/* + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + * Copyright (C) 2006 Paul Sokolosvky + * Based on code from older versions of htcuniversal_lcd.c + * + */ + +#include +#include +#include /* for pxa-regs.h (__REG) */ +#include +#include /* machine_is_htcuniversal */ +//#include +#include +#include + +#include +#include +#include +#include + +#define HTCUNIVERSAL_MAX_INTENSITY 0xc7 + +static void htcuniversal_set_bl_intensity(int intensity) +{ + PWM_CTRL1 = 1; /* pre-scaler */ + PWM_PWDUTY1 = intensity; /* duty cycle */ + PWM_PERVAL1 = HTCUNIVERSAL_MAX_INTENSITY+1; /* period */ + + if (intensity > 0) { + pxa_set_cken(CKEN_PWM1, 1); + asic3_set_gpio_out_d(&htcuniversal_asic3.dev, + (1<"); +MODULE_DESCRIPTION("Backlight driver for HTC Universal"); +MODULE_LICENSE("GPL"); Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.c 2008-03-10 16:09:23.000000000 +0000 @@ -0,0 +1,135 @@ +/* Bluetooth interface driver for TI BRF6150 on HX4700 + * + * Copyright (c) 2005 SDG Systems, LLC + * + * 2005-04-21 Todd Blumer Created. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "htcuniversal_bt.h" + +static uint use_led=1; + +static void +htcuniversal_bt_configure( int state ) +{ + int tries; + + printk( KERN_NOTICE "htcuniversal configure bluetooth: %d\n", state ); + switch (state) { + + case PXA_UART_CFG_PRE_STARTUP: + break; + + case PXA_UART_CFG_POST_STARTUP: + /* pre-serial-up hardware configuration */ + htcuniversal_egpio_enable(1< CTS=1). Typical 150ms + */ + tries = 0; + do { + mdelay(10); + } while ((BTMSR & MSR_CTS) == 0 && tries++ < 50); + if (use_led) { +// htcuniversal_set_led(2, 16, 16); + } + break; + + case PXA_UART_CFG_PRE_SHUTDOWN: + htcuniversal_egpio_disable(1<dev.platform_data; + + /* configure bluetooth UART */ + pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_BT_RXD_MD ); + pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_BT_TXD_MD ); + pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_BT_UART_CTS_MD ); + pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_BT_UART_RTS_MD ); + + funcs->configure = htcuniversal_bt_configure; + + /* Make sure the LED is off */ +// htcuniversal_clear_led(2); + + return 0; +} + +static int +htcuniversal_bt_remove( struct platform_device *dev ) +{ + struct htcuniversal_bt_funcs *funcs = dev->dev.platform_data; + + funcs->configure = NULL; + + /* Make sure the LED is off */ +// htcuniversal_clear_led(2); + + return 0; +} + +static struct platform_driver bt_driver = { + .driver = { + .name = "htcuniversal_bt", + }, + .probe = htcuniversal_bt_probe, + .remove = htcuniversal_bt_remove, +}; + +module_param(use_led, uint, 0); + +static int __init +htcuniversal_bt_init( void ) +{ + printk(KERN_NOTICE "htcuniversal Bluetooth Driver\n"); + return platform_driver_register( &bt_driver ); +} + +static void __exit +htcuniversal_bt_exit( void ) +{ + platform_driver_unregister( &bt_driver ); +} + +module_init( htcuniversal_bt_init ); +module_exit( htcuniversal_bt_exit ); + +MODULE_AUTHOR("Todd Blumer, SDG Systems, LLC"); +MODULE_DESCRIPTION("HTC Universal Bluetooth Support Driver"); +MODULE_LICENSE("GPL"); + +/* vim600: set noexpandtab sw=8 ts=8 :*/ + Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.h 2008-03-10 16:09:23.000000000 +0000 @@ -0,0 +1,17 @@ +/* + * Bluetooth support file for calling bluetooth configuration functions + * + * Copyright (c) 2005 SDG Systems, LLC + * + * 2005-06 Todd Blumer Initial Revision + */ + +#ifndef _HTCUNIVERSAL_BT_H +#define _HTCUNIVERSAL_BT_H + +struct htcuniversal_bt_funcs { + void (*configure) ( int state ); +}; + + +#endif Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_buttons.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_buttons.c 2008-03-10 16:09:23.000000000 +0000 @@ -0,0 +1,87 @@ +/* + * Buttons driver for HTC Universal + * + * This file is subject to the terms and conditions of the GNU General Public + * License. + * + * Copyright (C) 2005 Pawel Kolodziejski + * Copyright (C) 2003 Joshua Wise + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct asic3_keys_button asic3_buttons[] = { +//{KEY_SCREEN, ASIC3_GPIOA_IRQ_BASE+GPIOA_COVER_ROTATE_N, 1, "screen_cover", EV_SW}, +//{KEY_SWITCHVIDEOMODE, ASIC3_GPIOB_IRQ_BASE+GPIOB_CLAMSHELL_N, 1, "clamshell_rotate", EV_SW}, +//{KEY_KBDILLUMTOGGLE, ASIC3_GPIOB_IRQ_BASE+GPIOB_NIGHT_SENSOR, 1, "night_sensor", EV_SW}, +{SW_LID, ASIC3_GPIOA_IRQ_BASE+GPIOA_COVER_ROTATE_N, 1, "screen_cover", EV_SW}, +{SW_TABLET_MODE, ASIC3_GPIOB_IRQ_BASE+GPIOB_CLAMSHELL_N, 1, "clamshell_rotate", EV_SW}, +//{SW_NIGHT_SENSOR, ASIC3_GPIOB_IRQ_BASE+GPIOB_NIGHT_SENSOR, 1, "night_sensor", EV_SW}, +{KEY_F10, ASIC3_GPIOA_IRQ_BASE+GPIOA_BUTTON_BACKLIGHT_N, 1, "backlight_button"}, +{KEY_RECORD, ASIC3_GPIOA_IRQ_BASE+GPIOA_BUTTON_RECORD_N, 1, "record_button"}, +{KEY_CAMERA, ASIC3_GPIOA_IRQ_BASE+GPIOA_BUTTON_CAMERA_N, 1, "camera_button"}, +{KEY_VOLUMEDOWN, ASIC3_GPIOA_IRQ_BASE+GPIOA_VOL_UP_N, 1, "volume_slider_down"}, +{KEY_VOLUMEUP, ASIC3_GPIOA_IRQ_BASE+GPIOA_VOL_DOWN_N, 1, "volume_slider_up"}, +{KEY_KPENTER, ASIC3_GPIOD_IRQ_BASE+GPIOD_KEY_OK_N, 1, "select"}, +{KEY_RIGHT, ASIC3_GPIOD_IRQ_BASE+GPIOD_KEY_RIGHT_N, 1, "right"}, +{KEY_LEFT, ASIC3_GPIOD_IRQ_BASE+GPIOD_KEY_LEFT_N, 1, "left"}, +{KEY_DOWN, ASIC3_GPIOD_IRQ_BASE+GPIOD_KEY_DOWN_N, 1, "down"}, +{KEY_UP, ASIC3_GPIOD_IRQ_BASE+GPIOD_KEY_UP_N, 1, "up"}, +}; + +static struct asic3_keys_platform_data asic3_keys_data = { + .buttons = asic3_buttons, + .nbuttons = ARRAY_SIZE(asic3_buttons), + .asic3_dev = &htcuniversal_asic3.dev, +}; + +static struct platform_device htcuniversal_keys_asic3 = { + .name = "asic3-keys", + .dev = { .platform_data = &asic3_keys_data, } +}; + +static int __init htcuniversal_buttons_probe(struct platform_device *dev) +{ + platform_device_register(&htcuniversal_keys_asic3); + return 0; +} + +static struct platform_driver htcuniversal_buttons_driver = { + .driver = { + .name = "htcuniversal_buttons", + }, + .probe = htcuniversal_buttons_probe, +}; + +static int __init htcuniversal_buttons_init(void) +{ + if (!machine_is_htcuniversal()) + return -ENODEV; + + return platform_driver_register(&htcuniversal_buttons_driver); +} + +static void __exit htcuniversal_buttons_exit(void) +{ + platform_driver_unregister(&htcuniversal_buttons_driver); +} + +module_init(htcuniversal_buttons_init); +module_exit(htcuniversal_buttons_exit); + +MODULE_AUTHOR ("Joshua Wise, Pawel Kolodziejski, Paul Sokolosvky"); +MODULE_DESCRIPTION ("Buttons support for HTC Universal"); +MODULE_LICENSE ("GPL"); Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_core.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_core.c 2008-03-10 16:09:23.000000000 +0000 @@ -0,0 +1,226 @@ +/* Core Hardware driver for Hx4700 (Serial, ASIC3, EGPIOs) + * + * Copyright (c) 2005 SDG Systems, LLC + * + * 2005-03-29 Todd Blumer Converted basic structure to support hx4700 + * 2005-04-30 Todd Blumer Add IRDA code from H2200 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +volatile u_int16_t *egpios; +u_int16_t egpio_reg; + +static int htc_bootloader = 0; /* Is the stock HTC bootloader installed? */ + +/* + * may make sense to put egpios elsewhere, but they're here now + * since they share some of the same address space with the TI WLAN + * + * EGPIO register is write-only + */ + +void +htcuniversal_egpio_enable( u_int16_t bits ) +{ + unsigned long flags; + + local_irq_save(flags); + + egpio_reg |= bits; + *egpios = egpio_reg; + + local_irq_restore(flags); +} +EXPORT_SYMBOL_GPL(htcuniversal_egpio_enable); + +void +htcuniversal_egpio_disable( u_int16_t bits ) +{ + unsigned long flags; + + local_irq_save(flags); + + egpio_reg &= ~bits; + *egpios = egpio_reg; + + local_irq_restore(flags); +} +EXPORT_SYMBOL_GPL(htcuniversal_egpio_disable); + +#ifdef CONFIG_PM + +//void htcuniversal_ll_pm_init(void); + +static int htcuniversal_suspend(struct platform_device *dev, pm_message_t state) +{ + /* Turn off external clocks here, because htcuniversal_power and asic3_mmc + * scared to do so to not hurt each other. (-5 mA) */ + + + /* 0x20c2 is HTC clock value + * CLOCK_CDEX_SOURCE 2 + * CLOCK_CDEX_SPI 0 + * CLOCK_CDEX_OWM 0 + * + * CLOCK_CDEX_PWM0 0 + * CLOCK_CDEX_PWM1 0 + * CLOCK_CDEX_LED0 1 + * CLOCK_CDEX_LED1 1 + * + * CLOCK_CDEX_LED2 0 + * CLOCK_CDEX_SD_HOST 0 + * CLOCK_CDEX_SD_BUS 0 + * CLOCK_CDEX_SMBUS 0 + * + * CLOCK_CDEX_CONTROL_CX 0 + * CLOCK_CDEX_EX0 1 + * CLOCK_CDEX_EX1 0 + * */ + asic3_set_clock_cdex(&htcuniversal_asic3.dev, 0xffff, CLOCK_CDEX_SOURCE1 + |CLOCK_CDEX_LED0 + |CLOCK_CDEX_LED1 + |CLOCK_CDEX_LED2 + |CLOCK_CDEX_EX0 + |CLOCK_CDEX_EX1); + + *egpios = 0; /* turn off all egpio power */ + + /* Wake up enable. */ + PWER = PWER_GPIO0 + | PWER_GPIO1 /* reset */ + | PWER_GPIO9 /* USB */ + | PWER_GPIO10 /* AC on USB */ + | PWER_GPIO14 /* ASIC3 mux */ + | PWER_RTC; + /* Wake up on falling edge. */ + PFER = PWER_GPIO0 + | PWER_GPIO1 + | PWER_GPIO9 + | PWER_GPIO10 + | PWER_GPIO14; + + /* Wake up on rising edge. */ + PRER = PWER_GPIO0 + | PWER_GPIO1 + | PWER_GPIO9 + | PWER_GPIO10; + /* 3.6864 MHz oscillator power-down enable */ + PCFR = PCFR_OPDE | PCFR_PI2CEN | PCFR_GPROD | PCFR_GPR_EN; + + PGSR0 = 0x09088004; + PGSR1 = 0x00020002; + PGSR2 = 0x8001c000; + PGSR3 = 0x00106284; + + PSLR = 0xcc000000; + +#if 0 + /* + * If we're using bootldr and not the stock HTC bootloader, + * we want to wake up periodically to see if the charge is full while + * it is suspended. We do this with the OS timer 4 in the pxa270. + */ + if (!htc_bootloader) { + OMCR4 = 0x4b; /* Periodic, self-resetting, 1-second timer */ + OSMR4 = 5; /* Wake up bootldr after x seconds so it can + figure out what to do with the LEDs. */ + OIER |= 0x10; /* Enable interrupt source for Timer 4 */ + OSCR4 = 0; /* This starts the timer */ + } +#endif + + asic3_set_extcf_select(&htcuniversal_asic3.dev, ASIC3_EXTCF_OWM_EN, 0); + + return 0; +} + +static int htcuniversal_resume(struct platform_device *dev) +{ + htcuniversal_egpio_enable(0); + + return 0; +} +#else +# define htcuniversal_suspend NULL +# define htcuniversal_resume NULL +#endif + +static int +htcuniversal_core_probe( struct platform_device *dev ) +{ + + printk( KERN_NOTICE "HTC Universal Core Hardware Driver\n" ); + + egpios = (volatile u_int16_t *)ioremap_nocache(HTCUNIVERSAL_EGPIO_BASE, sizeof *egpios ); + if (!egpios) + return -ENODEV; + else + printk( KERN_NOTICE "HTC Universal Core: egpio at phy=0x%8.8x is at virt=0x%p\n", + HTCUNIVERSAL_EGPIO_BASE, egpios ); + + printk("Using stock HTC first stage bootloader\n"); + htc_bootloader = 1; + +// htcuniversal_ll_pm_init(); + + return 0; +} + +static int +htcuniversal_core_remove( struct platform_device *dev ) +{ + + if (egpios != NULL) + iounmap( (void *)egpios ); + + return 0; +} + +static struct platform_driver htcuniversal_core_driver = { + .driver = { + .name = "htcuniversal_core", + }, + .probe = htcuniversal_core_probe, + .remove = htcuniversal_core_remove, + .suspend = htcuniversal_suspend, + .resume = htcuniversal_resume, +}; + +static int __init +htcuniversal_core_init( void ) +{ + return platform_driver_register( &htcuniversal_core_driver ); +} + + +static void __exit +htcuniversal_core_exit( void ) +{ + platform_driver_unregister( &htcuniversal_core_driver ); +} + +module_init( htcuniversal_core_init ); +module_exit( htcuniversal_core_exit ); + +MODULE_AUTHOR("Todd Blumer, SDG Systems, LLC"); +MODULE_DESCRIPTION("HTC Universal Core Hardware Driver"); +MODULE_LICENSE("GPL"); + +/* vim600: set noexpandtab sw=8 ts=8 :*/ Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_lcd.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000