From 8a5f41de921e7454a0ee47c103b1063d792656e8 Mon Sep 17 00:00:00 2001 From: linjh Date: Thu, 27 Sep 2012 17:43:05 +0800 Subject: [PATCH] phonepad: commit phonepad(i30) audio switch code [reference file] modified: drivers/misc/Kconfig drivers/misc/Makefile new file: drivers/misc/rk2928_callpad_misc/Kconfig drivers/misc/rk2928_callpad_misc/Makefile drivers/misc/rk2928_callpad_misc/audio_switch.c drivers/misc/rk2928_callpad_misc/audio_switch.h drivers/misc/rk2928_callpad_misc/gpio_exp_callpad.c --- drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + drivers/misc/rk2928_callpad_misc/Kconfig | 15 + drivers/misc/rk2928_callpad_misc/Makefile | 2 + .../misc/rk2928_callpad_misc/audio_switch.c | 143 +++++++ .../misc/rk2928_callpad_misc/audio_switch.h | 5 + .../rk2928_callpad_misc/gpio_exp_callpad.c | 404 ++++++++++++++++++ 7 files changed, 571 insertions(+) create mode 100755 drivers/misc/rk2928_callpad_misc/Kconfig create mode 100755 drivers/misc/rk2928_callpad_misc/Makefile create mode 100755 drivers/misc/rk2928_callpad_misc/audio_switch.c create mode 100755 drivers/misc/rk2928_callpad_misc/audio_switch.h create mode 100755 drivers/misc/rk2928_callpad_misc/gpio_exp_callpad.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 0642367c33bd..964597fd1c49 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -552,5 +552,6 @@ source "drivers/misc/ti-st/Kconfig" source "drivers/misc/lis3lv02d/Kconfig" source "drivers/misc/carma/Kconfig" source "drivers/misc/3g_module/Kconfig" +source "drivers/misc/rk2928_callpad_misc/Kconfig" endif # MISC_DEVICES diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 0bbfe1e012f0..ea32927a092e 100755 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -58,3 +58,4 @@ obj-$(CONFIG_GPS_DEVICES) += gps/ obj-y += inv_mpu/ obj-$(CONFIG_TDSC8800) += tdsc8800.o obj-$(CONFIG_RK29_SC8800) += sc8800.o +obj-y += rk2928_callpad_misc/ diff --git a/drivers/misc/rk2928_callpad_misc/Kconfig b/drivers/misc/rk2928_callpad_misc/Kconfig new file mode 100755 index 000000000000..d11316f34634 --- /dev/null +++ b/drivers/misc/rk2928_callpad_misc/Kconfig @@ -0,0 +1,15 @@ +# +# rk2928 callpad misc device configuration +# + +menuconfig RK2928_CALLPAD_MISC + tristate "3G module for phonepad" + ---help--- + Say Y here if you have a support modem + +config AUDIO_SWITCH + bool "RK2928 callpad audio switch" + default n +config GPIOEXP_AW9523B + bool "RK2928 callpad gpio exp" + default n diff --git a/drivers/misc/rk2928_callpad_misc/Makefile b/drivers/misc/rk2928_callpad_misc/Makefile new file mode 100755 index 000000000000..05e5d7bcab57 --- /dev/null +++ b/drivers/misc/rk2928_callpad_misc/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_AUDIO_SWITCH) += audio_switch.o +obj-$(CONFIG_GPIOEXP_AW9523B) += gpio_exp_callpad.o diff --git a/drivers/misc/rk2928_callpad_misc/audio_switch.c b/drivers/misc/rk2928_callpad_misc/audio_switch.c new file mode 100755 index 000000000000..34c728808c64 --- /dev/null +++ b/drivers/misc/rk2928_callpad_misc/audio_switch.c @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +MODULE_LICENSE("GPL"); + +struct rk29_audio_switch_data { + struct device *dev; + unsigned int gpio_switch_fm_ap; + unsigned int gpio_switch_bb_ap; + int state; +}; + +struct rk29_audio_switch_data *gpdata = NULL; + +#define AUDIO_SWTICH_IO 0XA2 +#define AS_IOCTL_AP _IO(AUDIO_SWTICH_IO,0X01) +#define AS_IOCTL_BP _IO(AUDIO_SWTICH_IO,0X02) +#define AS_IOCTL_FM _IO(AUDIO_SWTICH_IO,0X03) + + +static int as_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int as_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static long as_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + + switch(cmd) + { + case AS_IOCTL_AP: + gpio_set_value(gpdata->gpio_switch_fm_ap, GPIO_LOW); + break; + case AS_IOCTL_BP: + break; + case AS_IOCTL_FM: + gpio_set_value(gpdata->gpio_switch_fm_ap, GPIO_HIGH); + break; + default: + break; + } + return 0; +} + +static struct file_operations as_fops = { + .owner = THIS_MODULE, + .open = as_open, + .release = as_release, + .unlocked_ioctl = as_ioctl +}; + +static struct miscdevice as_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "audio_switch", + .fops = &as_fops +}; + +static int as_probe(struct platform_device *pdev) +{ + int result=0; + gpdata = kzalloc(sizeof(struct rk29_audio_switch_data), GFP_KERNEL); + + rk30_mux_api_set(GPIO1B0_SPI_CLK_UART1_CTSN_NAME, GPIO1B_GPIO1B0); + gpdata->gpio_switch_fm_ap = gpio_request(RK2928_PIN1_PB0,"switch_bb_ap"); + gpio_direction_output(gpdata->gpio_switch_fm_ap,GPIO_LOW); + + platform_set_drvdata(pdev, gpdata); + + result = misc_register(&as_misc); + if(result){ + gpio_free(gpdata->gpio_switch_fm_ap); + kfree(gpdata); + } + return result; +} + +int as_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +int as_resume(struct platform_device *pdev) +{ + return 0; +} + +void as_shutdown(struct platform_device *pdev) +{ + gpio_free(gpdata->gpio_switch_fm_ap); + kfree(gpdata); +} + +static struct platform_driver as_driver = { + .probe = as_probe, + .shutdown = as_shutdown, + .suspend = as_suspend, + .resume = as_resume, + .driver = { + .name = "audio_switch", + .owner = THIS_MODULE, + }, +}; + +static int __init audio_switch_init(void) +{ + return platform_driver_register(&as_driver); +} + +static void __exit audio_switch_exit(void) +{ + platform_driver_unregister(&as_driver); +} + +module_init(audio_switch_init); + +module_exit(audio_switch_exit); diff --git a/drivers/misc/rk2928_callpad_misc/audio_switch.h b/drivers/misc/rk2928_callpad_misc/audio_switch.h new file mode 100755 index 000000000000..eaf1d5ed0e90 --- /dev/null +++ b/drivers/misc/rk2928_callpad_misc/audio_switch.h @@ -0,0 +1,5 @@ +#ifndef RK2928_AUDIO_SWTICH_H +#define RK2928_AUDIO_SWTICH_H + + +#endif diff --git a/drivers/misc/rk2928_callpad_misc/gpio_exp_callpad.c b/drivers/misc/rk2928_callpad_misc/gpio_exp_callpad.c new file mode 100755 index 000000000000..0af3c511a127 --- /dev/null +++ b/drivers/misc/rk2928_callpad_misc/gpio_exp_callpad.c @@ -0,0 +1,404 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define DEBUG_GPIO_EXP +#ifdef DEBUG_GPIO_EXP +#define DEBUGPRINT(x...) printk(x) +#else +#define DEBUGPRINT(x...) +#endif + +struct gpioexp_data { + struct i2c_client *client; + int irq; + struct work_struct gpioexp_work; + struct workqueue_struct *gpioexp_workqueue; + u16 int_mask_bit; + u16 pre_int_bit; + gpioexp_int_handler_t interrupt_handlers [GPIOEXP_PIN_MAX]; + unsigned long interrupt_trigger[GPIOEXP_PIN_MAX]; +}; + + +static struct gpioexp_data* gpioexp_ptr=NULL; + +int gpioexp_set_direction(unsigned gpio, int is_in) +{ + char buf[1]; + unsigned char reg, gpiobit; + + if(gpio >= GPIOEXP_PIN_MAX) + return -EINVAL; + + reg = (gpio<8) ? 0x04 : 0x05; + gpiobit = (gpio<8) ? (0x01<client, reg, buf, 1, 400*1000); + if(is_in) + buf[0] |= gpiobit; + else + buf[0] &= ~gpiobit; + + i2c_master_reg8_send(gpioexp_ptr->client, reg, buf, 1, 400*1000); + + DEBUGPRINT("gpioexp_set_direction:gpio = %d, buf[0] = %d\n", gpio, buf[0]); + + return 0; + +} +EXPORT_SYMBOL(gpioexp_set_direction); + +int gpioexp_set_output_level(unsigned gpio, int value) +{ + char buf[1]; + unsigned char reg, gpiobit; + + if(gpio >= GPIOEXP_PIN_MAX) + return -EINVAL; + + reg = (gpio<8) ? 0x02 : 0x03; + gpiobit = (gpio<8) ? (0x01<client, reg, buf, 1, 400*1000); + if(value) + buf[0] |= gpiobit; + else + buf[0] &= ~gpiobit; + + i2c_master_reg8_send(gpioexp_ptr->client, reg, buf, 1, 400*1000); + DEBUGPRINT("gpioexp_set_output_level:gpio = %d, buf[0] = %d\n", gpio, buf[0]); + + return 0; + +} +EXPORT_SYMBOL(gpioexp_set_output_level); + + +int gpioexp_read_input_level(unsigned gpio) +{ + char buf[1]; + unsigned char reg, gpiobit; + int ret; + + if(gpio >= GPIOEXP_PIN_MAX) + return -EINVAL; + + reg = (gpio<8) ? 0x00 : 0x01; + gpiobit = (gpio<8)?(0x01<client, reg, buf, 1, 400*1000); + + ret = (buf[0] & gpiobit)?1:0; + + DEBUGPRINT("gpioexp_read_input_level:gpio = %d, buf[0] = %d\n", gpio, buf[0]); + return ret; + +} +EXPORT_SYMBOL(gpioexp_read_input_level); + +int gpioexp_request_irq(unsigned int irq, gpioexp_int_handler_t handler, unsigned long flags) +{ + char buf[1]; + + if(irq > GPIOEXP_PIN_MAX || handler == NULL || !(flags & GPIOEXP_INT_TRIGGER_MASK)) + return -EINVAL; + + if(gpioexp_ptr->int_mask_bit & (0x01 << irq) == 0) + return -EBUSY; + gpioexp_ptr->interrupt_handlers[irq] = handler; + gpioexp_ptr->interrupt_trigger[irq] = flags & GPIOEXP_INT_TRIGGER_MASK; + gpioexp_ptr->int_mask_bit &= ~(0x01 << irq); + + if(irq >=8){ + buf[0] = (gpioexp_ptr->int_mask_bit >> 8) & 0xff; + i2c_master_reg8_send(gpioexp_ptr->client, 0x07, buf, 1, 400*1000); + } + else{ + buf[0] = gpioexp_ptr->int_mask_bit & 0xff; + i2c_master_reg8_send(gpioexp_ptr->client, 0x06, buf, 1, 400*1000); + } + + return 0; +} +EXPORT_SYMBOL(gpioexp_request_irq); + +int gpioexp_free_irq(unsigned int irq) +{ + char buf[1]; + + if(irq > GPIOEXP_PIN_MAX) + return -EINVAL; + if(gpioexp_ptr->int_mask_bit & (0x01 << irq) == 0){ + gpioexp_ptr->int_mask_bit &= ~(0x01 << irq); + if(irq >=8){ + buf[0] = (gpioexp_ptr->int_mask_bit >> 8) & 0xff; + i2c_master_reg8_send(gpioexp_ptr->client, 0x07, buf, 1, 400*1000); + } + else{ + buf[0] = gpioexp_ptr->int_mask_bit & 0xff; + i2c_master_reg8_send(gpioexp_ptr->client, 0x06, buf, 1, 400*1000); + } + } + gpioexp_ptr->interrupt_handlers[irq] = NULL; + gpioexp_ptr->interrupt_trigger[irq] = 0; + + return 0; +} EXPORT_SYMBOL(gpioexp_free_irq); + +int gpioexp_enable_irq(unsigned int irq) +{ + char buf[1]; + + if(irq > GPIOEXP_PIN_MAX) + return -EINVAL; + if(gpioexp_ptr->int_mask_bit & (0x01 << irq) == 0) + return 0; + gpioexp_ptr->int_mask_bit &= ~(0x01 << irq); + if(irq >=8){ + buf[0] = (gpioexp_ptr->int_mask_bit >> 8) & 0xff; + i2c_master_reg8_send(gpioexp_ptr->client, 0x07, buf, 1, 400*1000); + } + else{ + buf[0] = gpioexp_ptr->int_mask_bit & 0xff; + i2c_master_reg8_send(gpioexp_ptr->client, 0x06, buf, 1, 400*1000); + } + return 0; +} +EXPORT_SYMBOL(gpioexp_enable_irq); + +int gpioexp_disable_irq(unsigned int irq) +{ + char buf[1]; + + if(irq > GPIOEXP_PIN_MAX) + return -EINVAL; + if(gpioexp_ptr->int_mask_bit & (0x01 << irq) == 1) + return 0; + gpioexp_ptr->int_mask_bit |= (0x01 << irq); + if(irq >=8){ + buf[0] = (gpioexp_ptr->int_mask_bit >> 8) & 0xff; + i2c_master_reg8_send(gpioexp_ptr->client, 0x07, buf, 1, 400*1000); + } + else{ + buf[0] = gpioexp_ptr->int_mask_bit & 0xff; + i2c_master_reg8_send(gpioexp_ptr->client, 0x06, buf, 1, 400*1000); + } + return 0; +} +EXPORT_SYMBOL(gpioexp_disable_irq); + +static inline void gpioexp_handle_interrupt(struct gpioexp_data *data, u16 int_pending_bit) +{ + int i; + u16 pre_int_bit = data->pre_int_bit; + + for(i=0;i> i) & 0x01){ + if(data->interrupt_trigger[i] && GPIOEXP_INT_TRIGGER_FALLING)\ + data->interrupt_handlers[i](data); + } + else{ + if(data->interrupt_trigger[i] && GPIOEXP_INT_TRIGGER_RISING)\ + data->interrupt_handlers[i](data); + } + } + int_pending_bit = int_pending_bit >> 1; + } +} + +static inline u16 gpioexp_interrupt_pending(struct gpioexp_data *data,u16 cur_int_bit) +{ + u16 int_toggle_bit,int_pending_bit; + + int_toggle_bit = (data->pre_int_bit) ^ cur_int_bit; + int_pending_bit = (~data->int_mask_bit) & int_toggle_bit; + + return int_pending_bit; +} + +static void gpioexp_chip_init(void) +{ + char buf[1]; + + buf[0] = 0x10; //set P0 Push-Pull + i2c_master_reg8_send(gpioexp_ptr->client, 0x11, buf, 1, 400*1000); + + buf[0] = 0xff; //set interrupt disable + i2c_master_reg8_send(gpioexp_ptr->client, 0x06, buf, 1, 400*1000); + buf[0] = 0xff; //set interrupt disable + i2c_master_reg8_send(gpioexp_ptr->client, 0x07, buf, 1, 400*1000); + gpioexp_ptr->int_mask_bit = 0xffff; + + buf[0] = 0x00; //set input output P0 0b10111111 + i2c_master_reg8_send(gpioexp_ptr->client, 0x04, buf, 1, 400*1000); + buf[0] = 0x00; //set input output P1 0b01000000 + i2c_master_reg8_send(gpioexp_ptr->client, 0x05, buf, 1, 400*1000); + + buf[0] = 0x00; //set output value P0 0b00000000 + i2c_master_reg8_send(gpioexp_ptr->client, 0x02, buf, 1, 400*1000); + buf[0] = 0x80; //set output value P1 0b00000011 + i2c_master_reg8_send(gpioexp_ptr->client, 0x03, buf, 1, 400*1000); + gpioexp_ptr->pre_int_bit = 0x0000; + + DEBUGPRINT("gpioexp_init\n"); +} + +static void gpioexp_work(struct work_struct *work) +{ + struct gpioexp_data *data = container_of(work, struct gpioexp_data, gpioexp_work); + char buf[2] = {0}; + u16 cur_int_bit,int_pending_bit; + + DEBUGPRINT("gpioexp_work: read io status\n"); + + i2c_master_reg8_recv(gpioexp_ptr->client, 0x00, buf, 1, 400*1000); //read p0 value + i2c_master_reg8_recv(gpioexp_ptr->client, 0x01, buf+1, 1, 400*1000); //read p1 value + cur_int_bit = (u16)(buf[1]) << 8 | buf[0]; + int_pending_bit = gpioexp_interrupt_pending(data,cur_int_bit); + if(int_pending_bit){ + gpioexp_handle_interrupt(data,int_pending_bit); + } + data->pre_int_bit = cur_int_bit; + + enable_irq(data->irq); +} + +static irqreturn_t gpioexp_interrupt(int irq, void *dev_id) +{ + struct gpioexp_data *gpioexp = dev_id; + + DEBUGPRINT("gpioexp_interrupt\n"); + disable_irq_nosync(gpioexp->irq); + if (!work_pending(&gpioexp->gpioexp_work)) + queue_work(gpioexp->gpioexp_workqueue, &gpioexp->gpioexp_work); + return IRQ_HANDLED; +} + +static __devinit int gpioexp_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct gpio_exp_platform_data *pdata = i2c->dev.platform_data; + struct gpioexp_data *gpioexp; + int ret = 0; + + DEBUGPRINT("gpioexp_probe\n"); + if (!pdata) { + dev_err(&i2c->dev, "platform data is required!\n"); + return -EINVAL; + } + + gpioexp = kzalloc(sizeof(*gpioexp), GFP_KERNEL); + if (!gpioexp) { + return -ENOMEM; + } + + if (pdata->init_platform_hw) + pdata->init_platform_hw(); + + gpioexp->client = i2c; + gpioexp->irq = gpio_to_irq(i2c->irq); + + INIT_WORK(&gpioexp->gpioexp_work, gpioexp_work); + gpioexp->gpioexp_workqueue = create_singlethread_workqueue("gpioexp_workqueue"); + if (!gpioexp->gpioexp_workqueue) { + ret = -ESRCH; + goto gpioexp_wq_fail; + } + + + i2c_set_clientdata(gpioexp->client, gpioexp); + gpioexp_ptr = gpioexp; + + gpioexp_chip_init(); + + ret = request_irq(gpioexp->irq, gpioexp_interrupt, IRQF_TRIGGER_FALLING, gpioexp->client->dev.driver->name, gpioexp); + if (ret < 0) { + ret = 1; + goto gpioexp_irq_fail; + } + + DEBUGPRINT("gpioexp set modem power on!\n"); + return 0; + +gpioexp_irq_fail: + destroy_workqueue(gpioexp->gpioexp_workqueue); + i2c_set_clientdata(gpioexp->client, NULL); + gpioexp_ptr = NULL; +gpioexp_wq_fail: + pdata->exit_platform_hw(); + kfree(gpioexp); + + return ret; +} + +static __devexit int gpioexp_remove(struct i2c_client *client) +{ + struct gpio_exp_platform_data *pdata = client->dev.platform_data; + struct gpioexp_data *gpioexp = i2c_get_clientdata(client); + + DEBUGPRINT("gpioexp_remove"); + + destroy_workqueue(gpioexp->gpioexp_workqueue); + pdata->exit_platform_hw(); + kfree(gpioexp); + gpioexp_ptr = NULL; + + return 0; +} + +static const struct i2c_device_id gpioexp_i2c_id[] = { + { "gpioexp_aw9523b", 0 },{ } +}; + +static struct i2c_driver gpioexp_driver = { + .probe = gpioexp_probe, + .remove = __devexit_p(gpioexp_remove), + .id_table = gpioexp_i2c_id, + .driver = { + .name = "gpioexp_aw9523b", + .owner = THIS_MODULE, + }, +}; + +static int __init gpioexp_init(void) +{ + return i2c_add_driver(&gpioexp_driver); +} + +static void __exit gpioexp_exit(void) +{ + i2c_del_driver(&gpioexp_driver); +} + +subsys_initcall_sync(gpioexp_init); +module_exit(gpioexp_exit); + +MODULE_LICENSE("GPL"); + + -- 2.34.1