Commit e008466ba8da8924d3ffba519dfa4257e4f388f8
- Diff rendering mode:
- inline
- side by side
arch/arm/mach-msm/Makefile
(1 / 1)
|   | |||
| 39 | 39 | obj-$(CONFIG_MACH_HTCDIAMOND_CDMA) += board-htcdiamond.o board-htcraphael-navi.o board-htcraphael-panel.o htc_wifi_nvs.o mmc.o msm_vibrator.o msm-gpio-keys.o pmem.o htc_acoustic.o | |
| 40 | 40 | ||
| 41 | 41 | obj-$(CONFIG_MACH_HTCBLACKSTONE) += board-htcblackstone.o board-htcblackstone-panel.o htc_wifi_nvs.o mmc.o pmem.o | |
| 42 | obj-$(CONFIG_MACH_HTCBLACKSTONE) += msm_vibrator.o msm-gpio-keys.o htc_acoustic.o | ||
| 42 | obj-$(CONFIG_MACH_HTCBLACKSTONE) += msm_vibrator.o msm-gpio-keys.o htc_acoustic.o htc_headset_mgr.o htc_headset_gpio.o | ||
| 43 | 43 | ||
| 44 | 44 | obj-$(CONFIG_MACH_HTCKOVSKY) += board-htckovsky.o board-htckovsky-panel.o htc_wifi_nvs.o mmc.o pmem.o | |
| 45 | 45 | obj-$(CONFIG_MACH_HTCKOVSKY) += msm_vibrator.o msm-gpio-keys.o htc_acoustic.o |
|   | |||
| 41 | 41 | #include <mach/msm_serial_hs.h> | |
| 42 | 42 | #include <mach/vreg.h> | |
| 43 | 43 | #include <mach/htc_battery.h> | |
| 44 | #include <mach/htc_headset_mgr.h> | ||
| 45 | #include <mach/htc_headset_gpio.h> | ||
| 44 | 46 | ||
| 45 | 47 | #include <mach/gpio.h> | |
| 46 | 48 | #include <mach/io.h> | |
| … | … | ||
| 174 | 174 | .id = -1, | |
| 175 | 175 | }; | |
| 176 | 176 | ||
| 177 | |||
| 178 | static struct htc_headset_mgr_platform_data htc_headset_mgr_data = { | ||
| 179 | }; | ||
| 180 | |||
| 181 | static struct platform_device htc_headset_mgr = { | ||
| 182 | .name = "HTC_HEADSET_MGR", | ||
| 183 | .id = -1, | ||
| 184 | .dev = { | ||
| 185 | .platform_data = &htc_headset_mgr_data, | ||
| 186 | }, | ||
| 187 | }; | ||
| 188 | |||
| 189 | static struct htc_headset_gpio_platform_data htc_headset_gpio_data = { | ||
| 190 | .hpin_gpio = BLAC100_GPIO_HDS_DET, | ||
| 191 | .mic_detect_gpio = RAPH100_EXTMIC_IRQ, | ||
| 192 | .microp_channel = 1, | ||
| 193 | .key_enable_gpio = NULL, | ||
| 194 | .mic_select_gpio = NULL, | ||
| 195 | }; | ||
| 196 | |||
| 197 | static struct platform_device htc_headset_gpio = { | ||
| 198 | .name = "HTC_HEADSET_GPIO", | ||
| 199 | .id = -1, | ||
| 200 | .dev = { | ||
| 201 | .platform_data = &htc_headset_gpio_data, | ||
| 202 | }, | ||
| 203 | }; | ||
| 204 | |||
| 205 | |||
| 177 | 206 | static struct platform_device *devices[] __initdata = { | |
| 178 | 207 | &raphael_rfkill, | |
| 179 | 208 | &msm_device_smd, | |
| … | … | ||
| 216 | 216 | #endif | |
| 217 | 217 | &msm_device_htc_battery, | |
| 218 | 218 | &blac_snd, | |
| 219 | &htc_headset_mgr, | ||
| 220 | &htc_headset_gpio, | ||
| 219 | 221 | }; | |
| 220 | 222 | ||
| 221 | 223 | extern struct sys_timer msm_timer; |
|   | |||
| 92 | 92 | ||
| 93 | 93 | #define RAPH100_CAM1_RST 92 | |
| 94 | 94 | #define RAPH100_CAM2_RST 93 | |
| 95 | #define RAPH100_NAVI_IRQ 94 | ||
| 95 | #define BLAC100_GPIO_HDS_DET 94 | ||
| 96 | 96 | ||
| 97 | 97 | #define RAPH100_LCD_VSYNC 97 /* FIXME! hardcoded */ | |
| 98 | 98 | #define RAPH100_LCD_PWR1 98 |
arch/arm/mach-msm/htc_headset_gpio.c
(421 / 0)
|   | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * /arch/arm/mach-msm/htc_headset_gpio.c | ||
| 4 | * | ||
| 5 | * HTC GPIO headset detection driver. | ||
| 6 | * | ||
| 7 | * Copyright (C) 2010 HTC, Inc. | ||
| 8 | * | ||
| 9 | * This software is licensed under the terms of the GNU General Public | ||
| 10 | * License version 2, as published by the Free Software Foundation, and | ||
| 11 | * may be copied, distributed, and modified under those terms. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/sysdev.h> | ||
| 22 | #include <linux/device.h> | ||
| 23 | #include <linux/interrupt.h> | ||
| 24 | #include <linux/irq.h> | ||
| 25 | #include <linux/types.h> | ||
| 26 | #include <linux/input.h> | ||
| 27 | #include <linux/platform_device.h> | ||
| 28 | #include <linux/errno.h> | ||
| 29 | #include <linux/err.h> | ||
| 30 | #include <linux/hrtimer.h> | ||
| 31 | #include <linux/workqueue.h> | ||
| 32 | #include <linux/wakelock.h> | ||
| 33 | #include <linux/delay.h> | ||
| 34 | #include <linux/gpio.h> | ||
| 35 | |||
| 36 | #include <mach/htc_headset_mgr.h> | ||
| 37 | #include <mach/htc_headset_gpio.h> | ||
| 38 | |||
| 39 | #define DRIVER_NAME "HS_GPIO" | ||
| 40 | |||
| 41 | /* #define DEBUG */ | ||
| 42 | |||
| 43 | #ifdef DEBUG | ||
| 44 | #define AJ_DBG(fmt, arg...) \ | ||
| 45 | printk(KERN_INFO "[Audio Jack] %s " fmt "\r\n", __func__, ## arg) | ||
| 46 | #else | ||
| 47 | #define AJ_DBG(fmt, arg...) do {} while (0) | ||
| 48 | #endif | ||
| 49 | |||
| 50 | struct audio_jack_info { | ||
| 51 | unsigned int irq_jack; | ||
| 52 | unsigned int irq_mic; | ||
| 53 | int audio_jack_detect; | ||
| 54 | int key_enable_gpio; | ||
| 55 | int mic_select_gpio; | ||
| 56 | int audio_jack_flag; | ||
| 57 | int mic_detect; | ||
| 58 | int last_pressed_key; | ||
| 59 | int microp_channel; | ||
| 60 | |||
| 61 | struct hrtimer detection_timer; | ||
| 62 | ktime_t debounce_time; | ||
| 63 | |||
| 64 | struct work_struct work; | ||
| 65 | struct work_struct mic_work; | ||
| 66 | spinlock_t spin_lock; | ||
| 67 | |||
| 68 | struct wake_lock audiojack_wake_lock; | ||
| 69 | }; | ||
| 70 | |||
| 71 | static struct audio_jack_info *pjack_info; | ||
| 72 | |||
| 73 | static int hs_gpio_get_mic(void) | ||
| 74 | { | ||
| 75 | int value; | ||
| 76 | value = !gpio_get_value(pjack_info->mic_detect); | ||
| 77 | |||
| 78 | printk("hs_gpio_get_mic: %d\n", value); | ||
| 79 | return value; | ||
| 80 | } | ||
| 81 | |||
| 82 | static int hs_enable_key_irq(int status) | ||
| 83 | { | ||
| 84 | printk("hs_enable_key_irq: %d\n", status); | ||
| 85 | if(status) | ||
| 86 | enable_irq(pjack_info->irq_mic); | ||
| 87 | else | ||
| 88 | disable_irq(pjack_info->irq_mic); | ||
| 89 | return 0; | ||
| 90 | } | ||
| 91 | |||
| 92 | void hs_gpio_key_enable(int enable) | ||
| 93 | { | ||
| 94 | DBG_MSG(); | ||
| 95 | |||
| 96 | if (pjack_info->key_enable_gpio) | ||
| 97 | gpio_set_value(pjack_info->key_enable_gpio, enable); | ||
| 98 | } | ||
| 99 | |||
| 100 | void hs_gpio_mic_select(int enable) | ||
| 101 | { | ||
| 102 | DBG_MSG(); | ||
| 103 | |||
| 104 | if (pjack_info->mic_select_gpio) | ||
| 105 | gpio_set_value(pjack_info->mic_select_gpio, enable); | ||
| 106 | } | ||
| 107 | |||
| 108 | static int get_remote_keycode(int *keycode) | ||
| 109 | { | ||
| 110 | uint32_t val; | ||
| 111 | uint32_t btn = 0; | ||
| 112 | |||
| 113 | /*microp_set_adc_req(pjack_info->microp_channel); | ||
| 114 | if (microp_get_remote_adc(&val)) | ||
| 115 | { | ||
| 116 | // failed. who know why? ignore | ||
| 117 | *keycode = 0; | ||
| 118 | return 1; | ||
| 119 | }*/ | ||
| 120 | |||
| 121 | if((val >= 0) && (val <= 33)) | ||
| 122 | { | ||
| 123 | btn = 1; | ||
| 124 | } | ||
| 125 | else if((val >= 38) && (val <= 82)) | ||
| 126 | { | ||
| 127 | btn = 2; | ||
| 128 | } | ||
| 129 | else if((val >= 95) && (val <= 200)) | ||
| 130 | { | ||
| 131 | btn = 3; | ||
| 132 | } | ||
| 133 | else if(val > 200) | ||
| 134 | { | ||
| 135 | // check previous key | ||
| 136 | if (pjack_info->last_pressed_key) | ||
| 137 | { | ||
| 138 | *keycode = pjack_info->last_pressed_key | 0x80; | ||
| 139 | pjack_info->last_pressed_key = 0; | ||
| 140 | return 0; | ||
| 141 | } | ||
| 142 | *keycode = 0; | ||
| 143 | return 1; | ||
| 144 | } | ||
| 145 | |||
| 146 | pjack_info->last_pressed_key = btn; | ||
| 147 | *keycode = btn; | ||
| 148 | return 0; | ||
| 149 | } | ||
| 150 | |||
| 151 | static irqreturn_t mic_irq_handler(int irq, void *dev_id) | ||
| 152 | { | ||
| 153 | pr_info("MIC IRQ Handler\n"); | ||
| 154 | int value1, value2; | ||
| 155 | int retry_limit = 10; | ||
| 156 | |||
| 157 | AJ_DBG(""); | ||
| 158 | |||
| 159 | do { | ||
| 160 | value1 = gpio_get_value(pjack_info->mic_detect); | ||
| 161 | set_irq_type(pjack_info->irq_mic, value1 ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); | ||
| 162 | value2 = gpio_get_value(pjack_info->mic_detect); | ||
| 163 | } while (value1 != value2 && retry_limit-- > 0); | ||
| 164 | |||
| 165 | AJ_DBG("value2 = %d (%d retries)", value2, (10-retry_limit)); | ||
| 166 | |||
| 167 | schedule_work(&pjack_info->mic_work); | ||
| 168 | return IRQ_HANDLED; | ||
| 169 | } | ||
| 170 | |||
| 171 | static irqreturn_t detect_irq_handler(int irq, void *dev_id) | ||
| 172 | { | ||
| 173 | pr_info("DET IRQ Handler\n"); | ||
| 174 | int value1, value2; | ||
| 175 | int retry_limit = 10; | ||
| 176 | |||
| 177 | hs_notify_hpin_irq(); | ||
| 178 | |||
| 179 | AJ_DBG(""); | ||
| 180 | |||
| 181 | do { | ||
| 182 | value1 = gpio_get_value(pjack_info->audio_jack_detect); | ||
| 183 | set_irq_type(pjack_info->irq_jack, value1 ? | ||
| 184 | IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); | ||
| 185 | value2 = gpio_get_value(pjack_info->audio_jack_detect); | ||
| 186 | } while (value1 != value2 && retry_limit-- > 0); | ||
| 187 | |||
| 188 | AJ_DBG("value2 = %d (%d retries)", value2, (10-retry_limit)); | ||
| 189 | |||
| 190 | if ((pjack_info->audio_jack_flag == 0) ^ value2) { | ||
| 191 | wake_lock_timeout(&pjack_info->audiojack_wake_lock, 4*HZ); | ||
| 192 | |||
| 193 | /* Do the rest of the work in timer context */ | ||
| 194 | hrtimer_start(&pjack_info->detection_timer, | ||
| 195 | pjack_info->debounce_time, HRTIMER_MODE_REL); | ||
| 196 | } | ||
| 197 | |||
| 198 | return IRQ_HANDLED; | ||
| 199 | } | ||
| 200 | |||
| 201 | static enum hrtimer_restart detect_35mm_event_timer_func(struct hrtimer *data) | ||
| 202 | { | ||
| 203 | int state; | ||
| 204 | |||
| 205 | AJ_DBG(""); | ||
| 206 | state = !gpio_get_value(pjack_info->audio_jack_detect); | ||
| 207 | if (pjack_info->audio_jack_flag != state) { | ||
| 208 | pjack_info->audio_jack_flag = state; | ||
| 209 | schedule_work(&pjack_info->work); | ||
| 210 | } | ||
| 211 | |||
| 212 | return HRTIMER_NORESTART; | ||
| 213 | } | ||
| 214 | |||
| 215 | static void mic_work_func(struct work_struct *work) | ||
| 216 | { | ||
| 217 | pr_info("MIC Schedule Work\n"); | ||
| 218 | int keycode = 0; | ||
| 219 | |||
| 220 | printk("mic_intr_work_func\n"); | ||
| 221 | if (get_remote_keycode(&keycode) == 0) | ||
| 222 | { | ||
| 223 | printk("keycode %d\n", keycode); | ||
| 224 | htc_35mm_remote_notify_button_status(keycode); | ||
| 225 | } | ||
| 226 | else | ||
| 227 | printk("mic error keycode\n"); | ||
| 228 | |||
| 229 | } | ||
| 230 | |||
| 231 | static void audiojack_work_func(struct work_struct *work) | ||
| 232 | { | ||
| 233 | int is_insert; | ||
| 234 | pr_info("DET Schedule Work\n"); | ||
| 235 | unsigned long flags = 0; | ||
| 236 | |||
| 237 | spin_lock_irqsave(&pjack_info->spin_lock, flags); | ||
| 238 | is_insert = pjack_info->audio_jack_flag; | ||
| 239 | spin_unlock_irqrestore(&pjack_info->spin_lock, flags); | ||
| 240 | |||
| 241 | htc_35mm_remote_notify_insert_ext_headset(is_insert); | ||
| 242 | |||
| 243 | if (is_insert) | ||
| 244 | pjack_info->debounce_time = ktime_set(0, 200000000); | ||
| 245 | else | ||
| 246 | pjack_info->debounce_time = ktime_set(0, 500000000); | ||
| 247 | } | ||
| 248 | |||
| 249 | static void hs_gpio_register(void) | ||
| 250 | { | ||
| 251 | struct headset_notifier notifier; | ||
| 252 | |||
| 253 | if (pjack_info->mic_select_gpio) { | ||
| 254 | notifier.id = HEADSET_REG_MIC_SELECT; | ||
| 255 | notifier.func = hs_gpio_mic_select; | ||
| 256 | headset_notifier_register(¬ifier); | ||
| 257 | } | ||
| 258 | |||
| 259 | if (pjack_info->key_enable_gpio) { | ||
| 260 | notifier.id = HEADSET_REG_KEY_ENABLE; | ||
| 261 | notifier.func = hs_gpio_key_enable; | ||
| 262 | headset_notifier_register(¬ifier); | ||
| 263 | } | ||
| 264 | |||
| 265 | if (pjack_info->mic_detect) { | ||
| 266 | notifier.id = HEADSET_REG_MIC_STATUS; | ||
| 267 | notifier.func = hs_gpio_get_mic; | ||
| 268 | headset_notifier_register(¬ifier); | ||
| 269 | |||
| 270 | notifier.id = HEADSET_REG_KEY_INT_ENABLE; | ||
| 271 | notifier.func = hs_enable_key_irq; | ||
| 272 | headset_notifier_register(¬ifier); | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | static int audiojack_probe(struct platform_device *pdev) | ||
| 277 | { | ||
| 278 | int ret; | ||
| 279 | struct htc_headset_gpio_platform_data *pdata = pdev->dev.platform_data; | ||
| 280 | |||
| 281 | SYS_MSG("++++++++++++++++++++"); | ||
| 282 | |||
| 283 | pjack_info = kzalloc(sizeof(struct audio_jack_info), GFP_KERNEL); | ||
| 284 | if (!pjack_info) | ||
| 285 | return -ENOMEM; | ||
| 286 | |||
| 287 | pjack_info->audio_jack_detect = pdata->hpin_gpio; | ||
| 288 | pjack_info->key_enable_gpio = pdata->key_enable_gpio; | ||
| 289 | pjack_info->mic_select_gpio = pdata->mic_select_gpio; | ||
| 290 | pjack_info->mic_detect = pdata->mic_detect_gpio; | ||
| 291 | pjack_info->microp_channel = pdata->microp_channel; | ||
| 292 | pjack_info->audio_jack_flag = 0; | ||
| 293 | pjack_info->last_pressed_key = 0; | ||
| 294 | |||
| 295 | pjack_info->debounce_time = ktime_set(0, 500000000); | ||
| 296 | hrtimer_init(&pjack_info->detection_timer, | ||
| 297 | CLOCK_MONOTONIC, HRTIMER_MODE_REL); | ||
| 298 | pjack_info->detection_timer.function = detect_35mm_event_timer_func; | ||
| 299 | |||
| 300 | INIT_WORK(&pjack_info->work, audiojack_work_func); | ||
| 301 | INIT_WORK(&pjack_info->mic_work, mic_work_func); | ||
| 302 | |||
| 303 | spin_lock_init(&pjack_info->spin_lock); | ||
| 304 | wake_lock_init(&pjack_info->audiojack_wake_lock, | ||
| 305 | WAKE_LOCK_SUSPEND, "audiojack"); | ||
| 306 | |||
| 307 | if (pjack_info->audio_jack_detect) { | ||
| 308 | ret = gpio_request(pjack_info->audio_jack_detect, | ||
| 309 | "3.5mm_detect"); | ||
| 310 | if (ret < 0) | ||
| 311 | goto err_request_detect_gpio; | ||
| 312 | |||
| 313 | ret = gpio_direction_input(pjack_info->audio_jack_detect); | ||
| 314 | if (ret < 0) | ||
| 315 | goto err_set_detect_gpio; | ||
| 316 | |||
| 317 | pjack_info->irq_jack = | ||
| 318 | gpio_to_irq(pjack_info->audio_jack_detect); | ||
| 319 | if (pjack_info->irq_jack < 0) { | ||
| 320 | ret = pjack_info->irq_jack; | ||
| 321 | goto err_request_detect_irq; | ||
| 322 | } | ||
| 323 | |||
| 324 | ret = request_irq(pjack_info->irq_jack, | ||
| 325 | detect_irq_handler, | ||
| 326 | IRQF_TRIGGER_LOW, "35mm_headset", NULL); | ||
| 327 | if (ret < 0) | ||
| 328 | goto err_request_detect_irq; | ||
| 329 | |||
| 330 | ret = set_irq_wake(pjack_info->irq_jack, 1); | ||
| 331 | if (ret < 0) | ||
| 332 | goto err_set_irq_wake; | ||
| 333 | pr_info("DET IRQ Registered!"); | ||
| 334 | } | ||
| 335 | |||
| 336 | |||
| 337 | if (pjack_info->mic_detect) { | ||
| 338 | ret = gpio_request(pjack_info->mic_detect, | ||
| 339 | "mic_detect"); | ||
| 340 | if (ret < 0) | ||
| 341 | goto err_request_detect_gpio; | ||
| 342 | |||
| 343 | ret = gpio_direction_input(pjack_info->mic_detect); | ||
| 344 | if (ret < 0) | ||
| 345 | goto err_set_detect_gpio; | ||
| 346 | |||
| 347 | pjack_info->irq_mic = | ||
| 348 | gpio_to_irq(pjack_info->mic_detect); | ||
| 349 | if (pjack_info->irq_mic < 0) { | ||
| 350 | ret = pjack_info->irq_mic; | ||
| 351 | goto err_request_detect_irq; | ||
| 352 | } | ||
| 353 | |||
| 354 | ret = request_irq(pjack_info->irq_mic, | ||
| 355 | mic_irq_handler, IRQF_DISABLED | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW, "mic_headset", NULL); | ||
| 356 | if (ret < 0) | ||
| 357 | goto err_request_detect_irq; | ||
| 358 | |||
| 359 | disable_irq(pjack_info->irq_mic); | ||
| 360 | pr_info("MIC IRQ Registered!"); | ||
| 361 | } | ||
| 362 | hs_gpio_register(); | ||
| 363 | |||
| 364 | SYS_MSG("--------------------"); | ||
| 365 | |||
| 366 | return 0; | ||
| 367 | |||
| 368 | err_set_irq_wake: | ||
| 369 | if (pjack_info->audio_jack_detect) | ||
| 370 | free_irq(pjack_info->irq_jack, 0); | ||
| 371 | err_request_detect_irq: | ||
| 372 | err_set_detect_gpio: | ||
| 373 | if (pjack_info->audio_jack_detect) | ||
| 374 | gpio_free(pjack_info->audio_jack_detect); | ||
| 375 | err_request_detect_gpio: | ||
| 376 | printk(KERN_ERR "Audiojack: Failed in audiojack_probe\n"); | ||
| 377 | |||
| 378 | return ret; | ||
| 379 | } | ||
| 380 | |||
| 381 | static int audiojack_remove(struct platform_device *pdev) | ||
| 382 | { | ||
| 383 | if (pjack_info->audio_jack_detect) | ||
| 384 | free_irq(pjack_info->irq_jack, 0); | ||
| 385 | |||
| 386 | if (pjack_info->audio_jack_detect) | ||
| 387 | free_irq(pjack_info->irq_mic, 0); | ||
| 388 | |||
| 389 | if (pjack_info->audio_jack_detect) | ||
| 390 | gpio_free(pjack_info->audio_jack_detect); | ||
| 391 | |||
| 392 | if (pjack_info->audio_jack_detect) | ||
| 393 | gpio_free(pjack_info->mic_detect); | ||
| 394 | |||
| 395 | return 0; | ||
| 396 | } | ||
| 397 | |||
| 398 | static struct platform_driver audiojack_driver = { | ||
| 399 | .probe = audiojack_probe, | ||
| 400 | .remove = audiojack_remove, | ||
| 401 | .driver = { | ||
| 402 | .name = "HTC_HEADSET_GPIO", | ||
| 403 | .owner = THIS_MODULE, | ||
| 404 | }, | ||
| 405 | }; | ||
| 406 | |||
| 407 | static int __init audiojack_init(void) | ||
| 408 | { | ||
| 409 | return platform_driver_register(&audiojack_driver); | ||
| 410 | } | ||
| 411 | |||
| 412 | static void __exit audiojack_exit(void) | ||
| 413 | { | ||
| 414 | platform_driver_unregister(&audiojack_driver); | ||
| 415 | } | ||
| 416 | |||
| 417 | module_init(audiojack_init); | ||
| 418 | module_exit(audiojack_exit); | ||
| 419 | |||
| 420 | MODULE_DESCRIPTION("HTC GPIO headset detection driver"); | ||
| 421 | MODULE_LICENSE("GPL"); |
arch/arm/mach-msm/htc_headset_mgr.c
(925 / 0)
|   | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * /arch/arm/mach-msm/htc_headset_mgr.c | ||
| 4 | * | ||
| 5 | * HTC headset manager driver. | ||
| 6 | * | ||
| 7 | * Copyright (C) 2010 HTC, Inc. | ||
| 8 | * | ||
| 9 | * This software is licensed under the terms of the GNU General Public | ||
| 10 | * License version 2, as published by the Free Software Foundation, and | ||
| 11 | * may be copied, distributed, and modified under those terms. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/kernel.h> | ||
| 21 | #include <linux/module.h> | ||
| 22 | #include <linux/sysdev.h> | ||
| 23 | #include <linux/fs.h> | ||
| 24 | #include <linux/interrupt.h> | ||
| 25 | #include <linux/workqueue.h> | ||
| 26 | #include <linux/irq.h> | ||
| 27 | #include <linux/delay.h> | ||
| 28 | #include <linux/types.h> | ||
| 29 | #include <linux/platform_device.h> | ||
| 30 | #include <linux/mutex.h> | ||
| 31 | #include <linux/errno.h> | ||
| 32 | #include <linux/err.h> | ||
| 33 | #include <linux/hrtimer.h> | ||
| 34 | #include <linux/debugfs.h> | ||
| 35 | #include <linux/jiffies.h> | ||
| 36 | #include <linux/gpio.h> | ||
| 37 | |||
| 38 | #include <asm/atomic.h> | ||
| 39 | #include <asm/mach-types.h> | ||
| 40 | |||
| 41 | #include <mach/board.h> | ||
| 42 | #include <mach/vreg.h> | ||
| 43 | //#include <mach/atmega_microp.h> | ||
| 44 | |||
| 45 | #include <mach/htc_headset_mgr.h> | ||
| 46 | |||
| 47 | #define DRIVER_NAME "HS_MGR" | ||
| 48 | |||
| 49 | /* #define CONFIG_DEBUG_H2W */ | ||
| 50 | |||
| 51 | /*Delay 200ms when 11pin device plug in*/ | ||
| 52 | #define H2W_DETECT_DELAY msecs_to_jiffies(200) | ||
| 53 | #define BUTTON_H2W_DELAY msecs_to_jiffies(10) | ||
| 54 | |||
| 55 | #define H2WI(fmt, arg...) \ | ||
| 56 | printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg) | ||
| 57 | #define H2WE(fmt, arg...) \ | ||
| 58 | printk(KERN_ERR "[H2W] %s " fmt "\r\n", __func__, ## arg) | ||
| 59 | |||
| 60 | #ifdef CONFIG_DEBUG_H2W | ||
| 61 | #define H2W_DBG(fmt, arg...) \ | ||
| 62 | printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg) | ||
| 63 | #else | ||
| 64 | #define H2W_DBG(fmt, arg...) do {} while (0) | ||
| 65 | #endif | ||
| 66 | |||
| 67 | static struct workqueue_struct *detect_wq; | ||
| 68 | |||
| 69 | static void insert_35mm_do_work(struct work_struct *work); | ||
| 70 | static DECLARE_WORK(insert_35mm_work, insert_35mm_do_work); | ||
| 71 | static void remove_35mm_do_work(struct work_struct *work); | ||
| 72 | static DECLARE_WORK(remove_35mm_work, remove_35mm_do_work); | ||
| 73 | |||
| 74 | static struct workqueue_struct *button_wq; | ||
| 75 | |||
| 76 | static void button_35mm_do_work(struct work_struct *w); | ||
| 77 | static DECLARE_DELAYED_WORK(button_35mm_work, button_35mm_do_work); | ||
| 78 | |||
| 79 | static int hs_mgr_rpc_call(struct msm_rpc_server *server, | ||
| 80 | struct rpc_request_hdr *req, unsigned len); | ||
| 81 | |||
| 82 | static struct msm_rpc_server hs_rpc_server = { | ||
| 83 | .prog = HS_RPC_SERVER_PROG, | ||
| 84 | .vers = HS_RPC_SERVER_VERS, | ||
| 85 | .rpc_call = hs_mgr_rpc_call, | ||
| 86 | }; | ||
| 87 | |||
| 88 | struct button_work { | ||
| 89 | struct delayed_work key_work; | ||
| 90 | int key_code; | ||
| 91 | }; | ||
| 92 | |||
| 93 | static struct h2w_info *hi; | ||
| 94 | static struct hs_notifier_func hs_mgr_notifier; | ||
| 95 | |||
| 96 | void hs_notify_hpin_irq(void) | ||
| 97 | { | ||
| 98 | hi->hpin_jiffies = jiffies; | ||
| 99 | SYS_MSG("HPIN IRQ"); | ||
| 100 | } | ||
| 101 | |||
| 102 | int hs_hpin_stable(void) | ||
| 103 | { | ||
| 104 | unsigned long last_hpin_jiffies = 0; | ||
| 105 | unsigned long unstable_jiffies = 1.2 * HZ; | ||
| 106 | |||
| 107 | last_hpin_jiffies = hi->hpin_jiffies; | ||
| 108 | |||
| 109 | if (time_before_eq(jiffies, last_hpin_jiffies + unstable_jiffies)) | ||
| 110 | return 0; | ||
| 111 | |||
| 112 | return 1; | ||
| 113 | } | ||
| 114 | |||
| 115 | int headset_notifier_register(struct headset_notifier *notifier) | ||
| 116 | { | ||
| 117 | if (!notifier->func) { | ||
| 118 | SYS_MSG("NULL register function"); | ||
| 119 | return 0; | ||
| 120 | } | ||
| 121 | |||
| 122 | switch (notifier->id) { | ||
| 123 | case HEADSET_REG_REMOTE_ADC: | ||
| 124 | SYS_MSG("Register REMOTE_ADC notifier"); | ||
| 125 | hs_mgr_notifier.remote_adc = notifier->func; | ||
| 126 | break; | ||
| 127 | case HEADSET_REG_RPC_KEY: | ||
| 128 | SYS_MSG("Register RPC_KEY notifier"); | ||
| 129 | hs_mgr_notifier.rpc_key = notifier->func; | ||
| 130 | break; | ||
| 131 | case HEADSET_REG_MIC_STATUS: | ||
| 132 | SYS_MSG("Register MIC_STATUS notifier"); | ||
| 133 | hs_mgr_notifier.mic_status = notifier->func; | ||
| 134 | break; | ||
| 135 | case HEADSET_REG_MIC_BIAS: | ||
| 136 | SYS_MSG("Register MIC_BIAS notifier"); | ||
| 137 | hs_mgr_notifier.mic_bias_enable = notifier->func; | ||
| 138 | break; | ||
| 139 | case HEADSET_REG_MIC_SELECT: | ||
| 140 | SYS_MSG("Register MIC_SELECT notifier"); | ||
| 141 | hs_mgr_notifier.mic_select = notifier->func; | ||
| 142 | break; | ||
| 143 | case HEADSET_REG_KEY_INT_ENABLE: | ||
| 144 | SYS_MSG("Register KEY_INT_ENABLE notifier"); | ||
| 145 | hs_mgr_notifier.key_int_enable = notifier->func; | ||
| 146 | break; | ||
| 147 | case HEADSET_REG_KEY_ENABLE: | ||
| 148 | SYS_MSG("Register KEY_ENABLE notifier"); | ||
| 149 | hs_mgr_notifier.key_enable = notifier->func; | ||
| 150 | break; | ||
| 151 | default: | ||
| 152 | SYS_MSG("Unknown register ID"); | ||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | return 1; | ||
| 157 | } | ||
| 158 | |||
| 159 | static int hs_mgr_rpc_call(struct msm_rpc_server *server, | ||
| 160 | struct rpc_request_hdr *req, unsigned len) | ||
| 161 | { | ||
| 162 | struct hs_rpc_server_args_key *args_key; | ||
| 163 | |||
| 164 | DBG_MSG(""); | ||
| 165 | |||
| 166 | switch (req->procedure) { | ||
| 167 | case HS_RPC_SERVER_PROC_NULL: | ||
| 168 | SYS_MSG("RPC_SERVER_NULL"); | ||
| 169 | break; | ||
| 170 | case HS_RPC_SERVER_PROC_KEY: | ||
| 171 | args_key = (struct hs_rpc_server_args_key *)(req + 1); | ||
| 172 | args_key->adc = be32_to_cpu(args_key->adc); | ||
| 173 | SYS_MSG("RPC_SERVER_KEY ADC = %u (0x%X)", | ||
| 174 | args_key->adc, args_key->adc); | ||
| 175 | if (hs_mgr_notifier.rpc_key) | ||
| 176 | hs_mgr_notifier.rpc_key(args_key->adc); | ||
| 177 | else | ||
| 178 | SYS_MSG("RPC_KEY notify function doesn't exist"); | ||
| 179 | break; | ||
| 180 | default: | ||
| 181 | SYS_MSG("Unknown RPC procedure"); | ||
| 182 | return -EINVAL; | ||
| 183 | } | ||
| 184 | |||
| 185 | return 0; | ||
| 186 | } | ||
| 187 | |||
| 188 | static ssize_t h2w_print_name(struct switch_dev *sdev, char *buf) | ||
| 189 | { | ||
| 190 | return sprintf(buf, "Headset\n"); | ||
| 191 | } | ||
| 192 | |||
| 193 | void button_pressed(int type) | ||
| 194 | { | ||
| 195 | printk(KERN_INFO "[H2W] button_pressed %d\n", type); | ||
| 196 | atomic_set(&hi->btn_state, type); | ||
| 197 | input_report_key(hi->input, type, 1); | ||
| 198 | input_sync(hi->input); | ||
| 199 | } | ||
| 200 | |||
| 201 | void button_released(int type) | ||
| 202 | { | ||
| 203 | printk(KERN_INFO "[H2W] button_released %d\n", type); | ||
| 204 | atomic_set(&hi->btn_state, 0); | ||
| 205 | input_report_key(hi->input, type, 0); | ||
| 206 | input_sync(hi->input); | ||
| 207 | } | ||
| 208 | |||
| 209 | void Headset_button_event(int is_press, int type) | ||
| 210 | { | ||
| 211 | if (!hs_hpin_stable()) { | ||
| 212 | H2WI("The HPIN is unstable, SKIP THE BUTTON EVENT."); | ||
| 213 | return; | ||
| 214 | } | ||
| 215 | |||
| 216 | if (!is_press) { | ||
| 217 | if (hi->ignore_btn) | ||
| 218 | hi->ignore_btn = 0; | ||
| 219 | else | ||
| 220 | button_released(type); | ||
| 221 | } else { | ||
| 222 | if (!hi->ignore_btn && !atomic_read(&hi->btn_state)) | ||
| 223 | button_pressed(type); | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | static void set_35mm_hw_state(int state) | ||
| 228 | { | ||
| 229 | if (hi->mic_bias_state != state && hs_mgr_notifier.mic_bias_enable) { | ||
| 230 | hs_mgr_notifier.mic_bias_enable(state); | ||
| 231 | hi->mic_bias_state = state; | ||
| 232 | if (state) /* Wait for MIC bias stable */ | ||
| 233 | msleep(HS_DELAY_MIC_BIAS); | ||
| 234 | } | ||
| 235 | |||
| 236 | if (hs_mgr_notifier.mic_select) | ||
| 237 | hs_mgr_notifier.mic_select(state); | ||
| 238 | |||
| 239 | if (hs_mgr_notifier.key_enable) | ||
| 240 | hs_mgr_notifier.key_enable(state); | ||
| 241 | |||
| 242 | if (hs_mgr_notifier.key_int_enable) | ||
| 243 | hs_mgr_notifier.key_int_enable(state); | ||
| 244 | } | ||
| 245 | |||
| 246 | static void insert_11pin_35mm(int *state) | ||
| 247 | { | ||
| 248 | int mic = HEADSET_NO_MIC; | ||
| 249 | |||
| 250 | SYS_MSG("Insert USB 3.5mm headset"); | ||
| 251 | set_35mm_hw_state(1); | ||
| 252 | |||
| 253 | if (hs_mgr_notifier.mic_status) | ||
| 254 | mic = hs_mgr_notifier.mic_status(); | ||
| 255 | |||
| 256 | if (mic == HEADSET_NO_MIC) { | ||
| 257 | /* without microphone */ | ||
| 258 | *state |= BIT_HEADSET_NO_MIC; | ||
| 259 | hi->h2w_35mm_status = HTC_35MM_NO_MIC; | ||
| 260 | printk(KERN_INFO "11pin_3.5mm without microphone\n"); | ||
| 261 | } else { /* with microphone */ | ||
| 262 | *state |= BIT_HEADSET; | ||
| 263 | hi->h2w_35mm_status = HTC_35MM_MIC; | ||
| 264 | printk(KERN_INFO "11pin_3.5mm with microphone\n"); | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | static void remove_11pin_35mm(void) | ||
| 269 | { | ||
| 270 | SYS_MSG("Remove USB 3.5mm headset"); | ||
| 271 | |||
| 272 | set_35mm_hw_state(0); | ||
| 273 | |||
| 274 | if (atomic_read(&hi->btn_state)) | ||
| 275 | button_released(atomic_read(&hi->btn_state)); | ||
| 276 | hi->h2w_35mm_status = HTC_35MM_UNPLUG; | ||
| 277 | } | ||
| 278 | |||
| 279 | static void button_35mm_do_work(struct work_struct *w) | ||
| 280 | { | ||
| 281 | int key; | ||
| 282 | struct button_work *work; | ||
| 283 | |||
| 284 | work = container_of(w, struct button_work, key_work.work); | ||
| 285 | hi->key_level_flag = work->key_code; | ||
| 286 | |||
| 287 | if (!hi->is_ext_insert && !hi->h2w_35mm_status) { | ||
| 288 | kfree(work); | ||
| 289 | H2WI("3.5mm headset is plugged out, skip report key event"); | ||
| 290 | return; | ||
| 291 | } | ||
| 292 | |||
| 293 | if (hi->key_level_flag) { | ||
| 294 | switch (hi->key_level_flag) { | ||
| 295 | case 1: | ||
| 296 | H2WI("3.5mm RC: Play Pressed"); | ||
| 297 | key = HS_MGR_KEYCODE_MEDIA; | ||
| 298 | break; | ||
| 299 | case 2: | ||
| 300 | H2WI("3.5mm RC: BACKWARD Pressed"); | ||
| 301 | key = HS_MGR_KEYCODE_BACKWARD; | ||
| 302 | break; | ||
| 303 | case 3: | ||
| 304 | H2WI("3.5mm RC: FORWARD Pressed"); | ||
| 305 | key = HS_MGR_KEYCODE_FORWARD; | ||
| 306 | break; | ||
| 307 | default: | ||
| 308 | H2WI("3.5mm RC: WRONG Button Pressed"); | ||
| 309 | return; | ||
| 310 | } | ||
| 311 | Headset_button_event(1, key); | ||
| 312 | } else { /* key release */ | ||
| 313 | if (atomic_read(&hi->btn_state)) | ||
| 314 | Headset_button_event(0, atomic_read(&hi->btn_state)); | ||
| 315 | else | ||
| 316 | H2WI("3.5mm RC: WRONG Button Release"); | ||
| 317 | } | ||
| 318 | |||
| 319 | wake_lock_timeout(&hi->headset_wake_lock, 1.5 * HZ); | ||
| 320 | |||
| 321 | kfree(work); | ||
| 322 | } | ||
| 323 | |||
| 324 | static void enable_metrico_headset(int enable) | ||
| 325 | { | ||
| 326 | if (enable && !hi->metrico_status) { | ||
| 327 | #if 0 | ||
| 328 | enable_mos_test(1); | ||
| 329 | #endif | ||
| 330 | hi->metrico_status = 1; | ||
| 331 | printk(KERN_INFO "Enable metrico headset\n"); | ||
| 332 | } | ||
| 333 | |||
| 334 | if (!enable && hi->metrico_status) { | ||
| 335 | #if 0 | ||
| 336 | enable_mos_test(0); | ||
| 337 | #endif | ||
| 338 | hi->metrico_status = 0; | ||
| 339 | printk(KERN_INFO "Disable metrico headset\n"); | ||
| 340 | } | ||
| 341 | } | ||
| 342 | |||
| 343 | static void remove_35mm_do_work(struct work_struct *work) | ||
| 344 | { | ||
| 345 | int state; | ||
| 346 | |||
| 347 | wake_lock_timeout(&hi->headset_wake_lock, 2.5 * HZ); | ||
| 348 | |||
| 349 | H2W_DBG(""); | ||
| 350 | /*To solve the insert, remove, insert headset problem*/ | ||
| 351 | if (time_before_eq(jiffies, hi->insert_jiffies)) | ||
| 352 | msleep(800); | ||
| 353 | if (hi->is_ext_insert) { | ||
| 354 | H2WI("Skip 3.5mm headset plug out!!!"); | ||
| 355 | return; | ||
| 356 | } | ||
| 357 | |||
| 358 | SYS_MSG("Remove 3.5mm headset"); | ||
| 359 | set_35mm_hw_state(0); | ||
| 360 | |||
| 361 | /* For HW Metrico lab test */ | ||
| 362 | if (hi->metrico_status) | ||
| 363 | enable_metrico_headset(0); | ||
| 364 | |||
| 365 | if (atomic_read(&hi->btn_state)) | ||
| 366 | button_released(atomic_read(&hi->btn_state)); | ||
| 367 | hi->ext_35mm_status = HTC_35MM_UNPLUG; | ||
| 368 | |||
| 369 | mutex_lock(&hi->mutex_lock); | ||
| 370 | state = switch_get_state(&hi->sdev); | ||
| 371 | |||
| 372 | if (hi->usb_dev_type == USB_HEADSET) { | ||
| 373 | hi->usb_dev_status = STATUS_CONNECTED_ENABLED; | ||
| 374 | state &= ~(BIT_35MM_HEADSET | BIT_HEADSET); | ||
| 375 | state |= BIT_HEADSET_NO_MIC; | ||
| 376 | switch_set_state(&hi->sdev, state); | ||
| 377 | mutex_unlock(&hi->mutex_lock); | ||
| 378 | } else if (hi->usb_dev_type == H2W_TVOUT) { | ||
| 379 | state &= ~(BIT_HEADSET | BIT_35MM_HEADSET); | ||
| 380 | state |= BIT_HEADSET_NO_MIC; | ||
| 381 | switch_set_state(&hi->sdev, state); | ||
| 382 | #if 0 | ||
| 383 | } else if (hi->cable_in1 && !gpio_get_value(hi->cable_in1)) { | ||
| 384 | state &= ~BIT_35MM_HEADSET; | ||
| 385 | switch_set_state(&hi->sdev, state); | ||
| 386 | queue_delayed_work(detect_wq, &detect_h2w_work, | ||
| 387 | HS_DELAY_ZERO_JIFFIES); | ||
| 388 | #endif | ||
| 389 | } else { | ||
| 390 | state &= ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | | ||
| 391 | BIT_35MM_HEADSET); | ||
| 392 | switch_set_state(&hi->sdev, state); | ||
| 393 | } | ||
| 394 | |||
| 395 | mutex_unlock(&hi->mutex_lock); | ||
| 396 | } | ||
| 397 | |||
| 398 | static void insert_35mm_do_work(struct work_struct *work) | ||
| 399 | { | ||
| 400 | int state; | ||
| 401 | int i, mic1, mic2; | ||
| 402 | |||
| 403 | H2W_DBG(""); | ||
| 404 | hi->insert_jiffies = jiffies + HZ; | ||
| 405 | |||
| 406 | wake_lock_timeout(&hi->headset_wake_lock, 1.5 * HZ); | ||
| 407 | |||
| 408 | #if 0 | ||
| 409 | if (hi->usb_dev_type && hi->is_ext_insert && | ||
| 410 | hi->usb_dev_type != H2W_TVOUT && hi->usb_dev_type != USB_HEADSET) | ||
| 411 | remove_headset(); | ||
| 412 | else if (hi->usb_dev_type == USB_HEADSET) | ||
| 413 | hi->usb_dev_status = STATUS_CONNECTED_DISABLED; | ||
| 414 | #endif | ||
| 415 | |||
| 416 | if (hi->usb_dev_type == USB_HEADSET) | ||
| 417 | hi->usb_dev_status = STATUS_CONNECTED_DISABLED; | ||
| 418 | |||
| 419 | if (hi->is_ext_insert) { | ||
| 420 | SYS_MSG("Insert 3.5mm headset"); | ||
| 421 | set_35mm_hw_state(1); | ||
| 422 | hi->ignore_btn = 0; | ||
| 423 | |||
| 424 | mic1 = mic2 = HEADSET_NO_MIC; | ||
| 425 | if (hs_mgr_notifier.mic_status) { | ||
| 426 | if (hi->ext_35mm_status == HTC_35MM_NO_MIC || | ||
| 427 | hi->h2w_35mm_status == HTC_35MM_NO_MIC) | ||
| 428 | for (i = 0; i < 10; i++) { | ||
| 429 | mic1 = hs_mgr_notifier.mic_status(); | ||
| 430 | msleep(HS_DELAY_MIC_DETECT); | ||
| 431 | mic2 = hs_mgr_notifier.mic_status(); | ||
| 432 | if (mic1 == mic2) | ||
| 433 | break; | ||
| 434 | } | ||
| 435 | else | ||
| 436 | mic1 = mic2 = hs_mgr_notifier.mic_status(); | ||
| 437 | } | ||
| 438 | |||
| 439 | /* For HW Metrico lab test */ | ||
| 440 | if (mic2 == HEADSET_METRICO && !hi->metrico_status) | ||
| 441 | enable_metrico_headset(1); | ||
| 442 | |||
| 443 | mutex_lock(&hi->mutex_lock); | ||
| 444 | state = switch_get_state(&hi->sdev); | ||
| 445 | state &= ~(BIT_HEADSET | BIT_HEADSET_NO_MIC); | ||
| 446 | if (mic2 == HEADSET_NO_MIC || mic1 != mic2) { | ||
| 447 | state |= BIT_HEADSET_NO_MIC; | ||
| 448 | printk(KERN_INFO "3.5mm_headset without microphone\n"); | ||
| 449 | } else { | ||
| 450 | state |= BIT_HEADSET; | ||
| 451 | printk(KERN_INFO "3.5mm_headset with microphone\n"); | ||
| 452 | } | ||
| 453 | |||
| 454 | state |= BIT_35MM_HEADSET; | ||
| 455 | switch_set_state(&hi->sdev, state); | ||
| 456 | if (state & BIT_HEADSET_NO_MIC) | ||
| 457 | hi->ext_35mm_status = HTC_35MM_NO_MIC; | ||
| 458 | else | ||
| 459 | hi->ext_35mm_status = HTC_35MM_MIC; | ||
| 460 | mutex_unlock(&hi->mutex_lock); | ||
| 461 | } | ||
| 462 | } | ||
| 463 | |||
| 464 | int htc_35mm_remote_notify_insert_ext_headset(int insert) | ||
| 465 | { | ||
| 466 | if (hi) { | ||
| 467 | mutex_lock(&hi->mutex_lock); | ||
| 468 | hi->is_ext_insert = insert; | ||
| 469 | mutex_unlock(&hi->mutex_lock); | ||
| 470 | |||
| 471 | H2WI(" %d", hi->is_ext_insert); | ||
| 472 | if (!hi->is_ext_insert) | ||
| 473 | queue_work(detect_wq, &remove_35mm_work); | ||
| 474 | else | ||
| 475 | queue_work(detect_wq, &insert_35mm_work); | ||
| 476 | } | ||
| 477 | return 1; | ||
| 478 | } | ||
| 479 | |||
| 480 | int htc_35mm_remote_notify_microp_ready(void) | ||
| 481 | { | ||
| 482 | if (hi) { | ||
| 483 | if (hi->is_ext_insert) | ||
| 484 | queue_work(detect_wq, &insert_35mm_work); | ||
| 485 | #if 0 | ||
| 486 | if (hi->h2w_35mm_status) | ||
| 487 | insert_headset(NORMAL_HEARPHONE); | ||
| 488 | #endif | ||
| 489 | } | ||
| 490 | return 1; | ||
| 491 | } | ||
| 492 | |||
| 493 | int htc_35mm_remote_notify_button_status(int key_level) | ||
| 494 | { | ||
| 495 | struct button_work *work; | ||
| 496 | |||
| 497 | if (hi->ext_35mm_status == HTC_35MM_NO_MIC || | ||
| 498 | hi->h2w_35mm_status == HTC_35MM_NO_MIC) { | ||
| 499 | SYS_MSG("MIC re-detection"); | ||
| 500 | msleep(HS_DELAY_MIC_DETECT); | ||
| 501 | queue_work(detect_wq, &insert_35mm_work); | ||
| 502 | } else if (!hs_hpin_stable()) { | ||
| 503 | H2WI("The HPIN is unstable, SKIP THE BUTTON EVENT."); | ||
| 504 | return 1; | ||
| 505 | } else { | ||
| 506 | work = kzalloc(sizeof(struct button_work), GFP_KERNEL); | ||
| 507 | if (!work) { | ||
| 508 | printk(KERN_INFO "Failed to allocate button memory\n"); | ||
| 509 | return 1; | ||
| 510 | } | ||
| 511 | work->key_code = key_level; | ||
| 512 | INIT_DELAYED_WORK(&work->key_work, button_35mm_do_work); | ||
| 513 | queue_delayed_work(button_wq, &work->key_work, | ||
| 514 | HS_JIFFIES_BUTTON); | ||
| 515 | } | ||
| 516 | |||
| 517 | return 1; | ||
| 518 | } | ||
| 519 | |||
| 520 | static void usb_headset_detect(int type) | ||
| 521 | { | ||
| 522 | int state; | ||
| 523 | |||
| 524 | mutex_lock(&hi->mutex_lock); | ||
| 525 | state = switch_get_state(&hi->sdev); | ||
| 526 | |||
| 527 | switch (type) { | ||
| 528 | case NO_DEVICE: | ||
| 529 | if (hi->usb_dev_type == USB_HEADSET) { | ||
| 530 | printk(KERN_INFO "Remove USB headset\n"); | ||
| 531 | hi->usb_dev_type = NO_DEVICE; | ||
| 532 | hi->usb_dev_status = STATUS_DISCONNECTED; | ||
| 533 | state &= ~BIT_USB_HEADSET; | ||
| 534 | if (!hi->is_ext_insert) | ||
| 535 | state &= ~BIT_HEADSET_NO_MIC; | ||
| 536 | } | ||
| 537 | break; | ||
| 538 | case USB_HEADSET: | ||
| 539 | printk(KERN_INFO "Insert USB headset\n"); | ||
| 540 | hi->usb_dev_type = USB_HEADSET; | ||
| 541 | if (hi->is_ext_insert) { | ||
| 542 | printk(KERN_INFO "Disable USB headset\n"); | ||
| 543 | hi->usb_dev_status = STATUS_CONNECTED_DISABLED; | ||
| 544 | state |= BIT_USB_HEADSET; | ||
| 545 | } else { | ||
| 546 | printk(KERN_INFO "Enable USB headset\n"); | ||
| 547 | hi->usb_dev_status = STATUS_CONNECTED_ENABLED; | ||
| 548 | state |= (BIT_USB_HEADSET | BIT_HEADSET_NO_MIC); | ||
| 549 | } | ||
| 550 | break; | ||
| 551 | default: | ||
| 552 | printk(KERN_INFO "Unknown headset type\n"); | ||
| 553 | } | ||
| 554 | |||
| 555 | switch_set_state(&hi->sdev, state); | ||
| 556 | mutex_unlock(&hi->mutex_lock); | ||
| 557 | } | ||
| 558 | |||
| 559 | void headset_ext_detect(int type) | ||
| 560 | { | ||
| 561 | switch (type) { | ||
| 562 | case NO_DEVICE: | ||
| 563 | if (hi->usb_dev_type == USB_HEADSET) | ||
| 564 | usb_headset_detect(type); | ||
| 565 | break; | ||
| 566 | case USB_HEADSET: | ||
| 567 | usb_headset_detect(type); | ||
| 568 | break; | ||
| 569 | default: | ||
| 570 | printk(KERN_INFO "Unknown headset type\n"); | ||
| 571 | } | ||
| 572 | } | ||
| 573 | |||
| 574 | int switch_send_event(unsigned int bit, int on) | ||
| 575 | { | ||
| 576 | unsigned long state; | ||
| 577 | |||
| 578 | mutex_lock(&hi->mutex_lock); | ||
| 579 | state = switch_get_state(&hi->sdev); | ||
| 580 | state &= ~(bit); | ||
| 581 | |||
| 582 | if (on) | ||
| 583 | state |= bit; | ||
| 584 | |||
| 585 | switch_set_state(&hi->sdev, state); | ||
| 586 | mutex_unlock(&hi->mutex_lock); | ||
| 587 | return 0; | ||
| 588 | } | ||
| 589 | |||
| 590 | static ssize_t tty_flag_show(struct device *dev, | ||
| 591 | struct device_attribute *attr, char *buf) | ||
| 592 | { | ||
| 593 | char *s = buf; | ||
| 594 | mutex_lock(&hi->mutex_lock); | ||
| 595 | s += sprintf(s, "%d\n", hi->tty_enable_flag); | ||
| 596 | mutex_unlock(&hi->mutex_lock); | ||
| 597 | return (s - buf); | ||
| 598 | } | ||
| 599 | |||
| 600 | static ssize_t tty_flag_store(struct device *dev, | ||
| 601 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 602 | { | ||
| 603 | int state; | ||
| 604 | |||
| 605 | mutex_lock(&hi->mutex_lock); | ||
| 606 | state = switch_get_state(&hi->sdev); | ||
| 607 | state &= ~(BIT_TTY_FULL | BIT_TTY_VCO | BIT_TTY_HCO); | ||
| 608 | |||
| 609 | if (count == (strlen("enable") + 1) && | ||
| 610 | strncmp(buf, "enable", strlen("enable")) == 0) { | ||
| 611 | hi->tty_enable_flag = 1; | ||
| 612 | switch_set_state(&hi->sdev, state | BIT_TTY_FULL); | ||
| 613 | mutex_unlock(&hi->mutex_lock); | ||
| 614 | printk(KERN_INFO "Enable TTY FULL\n"); | ||
| 615 | return count; | ||
| 616 | } | ||
| 617 | if (count == (strlen("vco_enable") + 1) && | ||
| 618 | strncmp(buf, "vco_enable", strlen("vco_enable")) == 0) { | ||
| 619 | hi->tty_enable_flag = 2; | ||
| 620 | switch_set_state(&hi->sdev, state | BIT_TTY_VCO); | ||
| 621 | mutex_unlock(&hi->mutex_lock); | ||
| 622 | printk(KERN_INFO "Enable TTY VCO\n"); | ||
| 623 | return count; | ||
| 624 | } | ||
| 625 | if (count == (strlen("hco_enable") + 1) && | ||
| 626 | strncmp(buf, "hco_enable", strlen("hco_enable")) == 0) { | ||
| 627 | hi->tty_enable_flag = 3; | ||
| 628 | switch_set_state(&hi->sdev, state | BIT_TTY_HCO); | ||
| 629 | mutex_unlock(&hi->mutex_lock); | ||
| 630 | printk(KERN_INFO "Enable TTY HCO\n"); | ||
| 631 | return count; | ||
| 632 | } | ||
| 633 | if (count == (strlen("disable") + 1) && | ||
| 634 | strncmp(buf, "disable", strlen("disable")) == 0) { | ||
| 635 | hi->tty_enable_flag = 0; | ||
| 636 | switch_set_state(&hi->sdev, state); | ||
| 637 | mutex_unlock(&hi->mutex_lock); | ||
| 638 | printk(KERN_INFO "Disable TTY\n"); | ||
| 639 | return count; | ||
| 640 | } | ||
| 641 | printk(KERN_ERR "tty_enable_flag_store: invalid argument\n"); | ||
| 642 | return -EINVAL; | ||
| 643 | } | ||
| 644 | static DEVICE_ACCESSORY_ATTR(tty, 0666, tty_flag_show, tty_flag_store); | ||
| 645 | |||
| 646 | static ssize_t fm_flag_store(struct device *dev, | ||
| 647 | struct device_attribute *attr, const char *buf, size_t count) | ||
| 648 | { | ||
| 649 | int state; | ||
| 650 | |||
| 651 | mutex_lock(&hi->mutex_lock); | ||
| 652 | state = switch_get_state(&hi->sdev); | ||
| 653 | state &= ~(BIT_FM_HEADSET | BIT_FM_SPEAKER); | ||
| 654 | |||
| 655 | if (count == (strlen("fm_headset") + 1) && | ||
| 656 | strncmp(buf, "fm_headset", strlen("fm_headset")) == 0) { | ||
| 657 | hi->fm_flag = 1; | ||
| 658 | state |= BIT_FM_HEADSET; | ||
| 659 | printk(KERN_INFO "Enable FM HEADSET\n"); | ||
| 660 | } else if (count == (strlen("fm_speaker") + 1) && | ||
| 661 | strncmp(buf, "fm_speaker", strlen("fm_speaker")) == 0) { | ||
| 662 | hi->fm_flag = 2; | ||
| 663 | state |= BIT_FM_SPEAKER; | ||
| 664 | printk(KERN_INFO "Enable FM SPEAKER\n"); | ||
| 665 | } else if (count == (strlen("disable") + 1) && | ||
| 666 | strncmp(buf, "disable", strlen("disable")) == 0) { | ||
| 667 | hi->fm_flag = 0 ; | ||
| 668 | printk(KERN_INFO "Disable FM\n"); | ||
| 669 | } else { | ||
| 670 | mutex_unlock(&hi->mutex_lock); | ||
| 671 | printk(KERN_ERR "fm_enable_flag_store: invalid argument\n"); | ||
| 672 | return -EINVAL; | ||
| 673 | } | ||
| 674 | switch_set_state(&hi->sdev, state); | ||
| 675 | mutex_unlock(&hi->mutex_lock); | ||
| 676 | return count; | ||
| 677 | } | ||
| 678 | |||
| 679 | static ssize_t fm_flag_show(struct device *dev, | ||
| 680 | struct device_attribute *attr, char *buf) | ||
| 681 | { | ||
| 682 | char *s = buf; | ||
| 683 | char *show_str; | ||
| 684 | mutex_lock(&hi->mutex_lock); | ||
| 685 | if (hi->fm_flag == 0) | ||
| 686 | show_str = "disable"; | ||
| 687 | if (hi->fm_flag == 1) | ||
| 688 | show_str = "fm_headset"; | ||
| 689 | if (hi->fm_flag == 2) | ||
| 690 | show_str = "fm_speaker"; | ||
| 691 | |||
| 692 | s += sprintf(s, "%s\n", show_str); | ||
| 693 | mutex_unlock(&hi->mutex_lock); | ||
| 694 | return (s - buf); | ||
| 695 | } | ||
| 696 | static DEVICE_ACCESSORY_ATTR(fm, 0666, fm_flag_show, fm_flag_store); | ||
| 697 | |||
| 698 | static int register_common_headset(struct h2w_info *h2w, int create_attr) | ||
| 699 | { | ||
| 700 | int ret = 0; | ||
| 701 | hi = h2w; | ||
| 702 | |||
| 703 | hi->htc_accessory_class = class_create(THIS_MODULE, "htc_accessory"); | ||
| 704 | if (IS_ERR(hi->htc_accessory_class)) { | ||
| 705 | ret = PTR_ERR(hi->htc_accessory_class); | ||
| 706 | hi->htc_accessory_class = NULL; | ||
| 707 | goto err_create_class; | ||
| 708 | } | ||
| 709 | |||
| 710 | hi->tty_dev = device_create(hi->htc_accessory_class, | ||
| 711 | NULL, 0, "%s", "tty"); | ||
| 712 | if (unlikely(IS_ERR(hi->tty_dev))) { | ||
| 713 | ret = PTR_ERR(hi->tty_dev); | ||
| 714 | hi->tty_dev = NULL; | ||
| 715 | goto err_create_tty_device; | ||
| 716 | } | ||
| 717 | |||
| 718 | /* register the attributes */ | ||
| 719 | ret = device_create_file(hi->tty_dev, &dev_attr_tty); | ||
| 720 | if (ret) | ||
| 721 | goto err_create_tty_device_file; | ||
| 722 | |||
| 723 | hi->fm_dev = device_create(hi->htc_accessory_class, | ||
| 724 | NULL, 0, "%s", "fm"); | ||
| 725 | if (unlikely(IS_ERR(hi->fm_dev))) { | ||
| 726 | ret = PTR_ERR(hi->fm_dev); | ||
| 727 | hi->fm_dev = NULL; | ||
| 728 | goto err_create_fm_device; | ||
| 729 | } | ||
| 730 | |||
| 731 | /* register the attributes */ | ||
| 732 | ret = device_create_file(hi->fm_dev, &dev_attr_fm); | ||
| 733 | if (ret) | ||
| 734 | goto err_create_fm_device_file; | ||
| 735 | |||
| 736 | return 0; | ||
| 737 | |||
| 738 | err_create_fm_device_file: | ||
| 739 | device_unregister(hi->fm_dev); | ||
| 740 | err_create_fm_device: | ||
| 741 | device_remove_file(hi->tty_dev, &dev_attr_tty); | ||
| 742 | err_create_tty_device_file: | ||
| 743 | device_unregister(hi->tty_dev); | ||
| 744 | err_create_tty_device: | ||
| 745 | class_destroy(hi->htc_accessory_class); | ||
| 746 | err_create_class: | ||
| 747 | |||
| 748 | return ret; | ||
| 749 | } | ||
| 750 | |||
| 751 | static void unregister_common_headset(struct h2w_info *h2w) | ||
| 752 | { | ||
| 753 | hi = h2w; | ||
| 754 | device_remove_file(hi->tty_dev, &dev_attr_tty); | ||
| 755 | device_unregister(hi->tty_dev); | ||
| 756 | device_remove_file(hi->fm_dev, &dev_attr_fm); | ||
| 757 | device_unregister(hi->fm_dev); | ||
| 758 | class_destroy(hi->htc_accessory_class); | ||
| 759 | } | ||
| 760 | |||
| 761 | static int htc_35mm_probe(struct platform_device *pdev) | ||
| 762 | { | ||
| 763 | int ret; | ||
| 764 | |||
| 765 | struct htc_headset_mgr_platform_data *pdata = pdev->dev.platform_data; | ||
| 766 | |||
| 767 | SYS_MSG("++++++++++++++++++++"); | ||
| 768 | |||
| 769 | hi = kzalloc(sizeof(struct h2w_info), GFP_KERNEL); | ||
| 770 | if (!hi) | ||
| 771 | return -ENOMEM; | ||
| 772 | |||
| 773 | hi->driver_flag = pdata->driver_flag; | ||
| 774 | hi->hpin_jiffies = jiffies; | ||
| 775 | |||
| 776 | hi->ext_35mm_status = 0; | ||
| 777 | hi->h2w_35mm_status = 0; | ||
| 778 | hi->is_ext_insert = 0; | ||
| 779 | hi->mic_bias_state = 0; | ||
| 780 | hi->key_level_flag = -1; | ||
| 781 | |||
| 782 | atomic_set(&hi->btn_state, 0); | ||
| 783 | hi->ignore_btn = 0; | ||
| 784 | hi->usb_dev_type = NO_DEVICE; | ||
| 785 | hi->tty_enable_flag = 0; | ||
| 786 | hi->fm_flag = 0; | ||
| 787 | hi->mic_switch_flag = 1; | ||
| 788 | hi->rc_flag = 0; | ||
| 789 | |||
| 790 | hi->insert_11pin_35mm = insert_11pin_35mm; | ||
| 791 | hi->remove_11pin_35mm = remove_11pin_35mm; | ||
| 792 | |||
| 793 | mutex_init(&hi->mutex_lock); | ||
| 794 | mutex_init(&hi->mutex_rc_lock); | ||
| 795 | |||
| 796 | wake_lock_init(&hi->headset_wake_lock, WAKE_LOCK_SUSPEND, "headset"); | ||
| 797 | |||
| 798 | hi->sdev.name = "h2w"; | ||
| 799 | hi->sdev.print_name = h2w_print_name; | ||
| 800 | |||
| 801 | ret = switch_dev_register(&hi->sdev); | ||
| 802 | if (ret < 0) | ||
| 803 | goto err_switch_dev_register; | ||
| 804 | |||
| 805 | detect_wq = create_workqueue("detection"); | ||
| 806 | if (detect_wq == NULL) { | ||
| 807 | ret = -ENOMEM; | ||
| 808 | goto err_create_detect_work_queue; | ||
| 809 | } | ||
| 810 | button_wq = create_workqueue("button"); | ||
| 811 | if (button_wq == NULL) { | ||
| 812 | ret = -ENOMEM; | ||
| 813 | goto err_create_button_work_queue; | ||
| 814 | } | ||
| 815 | |||
| 816 | hi->input = input_allocate_device(); | ||
| 817 | if (!hi->input) { | ||
| 818 | ret = -ENOMEM; | ||
| 819 | goto err_request_input_dev; | ||
| 820 | } | ||
| 821 | |||
| 822 | hi->input->name = "h2w headset"; | ||
| 823 | set_bit(EV_SYN, hi->input->evbit); | ||
| 824 | set_bit(EV_KEY, hi->input->evbit); | ||
| 825 | set_bit(KEY_END, hi->input->keybit); | ||
| 826 | set_bit(KEY_MUTE, hi->input->keybit); | ||
| 827 | set_bit(KEY_VOLUMEDOWN, hi->input->keybit); | ||
| 828 | set_bit(KEY_VOLUMEUP, hi->input->keybit); | ||
| 829 | set_bit(KEY_NEXTSONG, hi->input->keybit); | ||
| 830 | set_bit(KEY_PLAYPAUSE, hi->input->keybit); | ||
| 831 | set_bit(KEY_PREVIOUSSONG, hi->input->keybit); | ||
| 832 | set_bit(KEY_MEDIA, hi->input->keybit); | ||
| 833 | set_bit(KEY_SEND, hi->input->keybit); | ||
| 834 | |||
| 835 | ret = input_register_device(hi->input); | ||
| 836 | if (ret < 0) | ||
| 837 | goto err_register_input_dev; | ||
| 838 | |||
| 839 | ret = register_common_headset(hi, 0); | ||
| 840 | if (ret) | ||
| 841 | goto err_register_common_headset; | ||
| 842 | |||
| 843 | if (hi->driver_flag & DRIVER_HS_MGR_RPC_SERVER) { | ||
| 844 | /* Create RPC server */ | ||
| 845 | ret = msm_rpc_create_server(&hs_rpc_server); | ||
| 846 | if (ret < 0) { | ||
| 847 | SYS_MSG("Failed to create RPC server"); | ||
| 848 | goto err_create_rpc_server; | ||
| 849 | } | ||
| 850 | SYS_MSG("Create RPC server successfully"); | ||
| 851 | } | ||
| 852 | |||
| 853 | SYS_MSG("--------------------"); | ||
| 854 | |||
| 855 | return 0; | ||
| 856 | |||
| 857 | err_create_rpc_server: | ||
| 858 | |||
| 859 | err_register_common_headset: | ||
| 860 | input_unregister_device(hi->input); | ||
| 861 | |||
| 862 | err_register_input_dev: | ||
| 863 | input_free_device(hi->input); | ||
| 864 | |||
| 865 | err_request_input_dev: | ||
| 866 | destroy_workqueue(button_wq); | ||
| 867 | |||
| 868 | err_create_button_work_queue: | ||
| 869 | destroy_workqueue(detect_wq); | ||
| 870 | |||
| 871 | err_create_detect_work_queue: | ||
| 872 | switch_dev_unregister(&hi->sdev); | ||
| 873 | |||
| 874 | err_switch_dev_register: | ||
| 875 | |||
| 876 | printk(KERN_ERR "H2W: Failed to register driver\n"); | ||
| 877 | |||
| 878 | return ret; | ||
| 879 | } | ||
| 880 | |||
| 881 | static int htc_35mm_remove(struct platform_device *pdev) | ||
| 882 | { | ||
| 883 | H2W_DBG(""); | ||
| 884 | |||
| 885 | #if 0 | ||
| 886 | if ((switch_get_state(&hi->sdev) & | ||
| 887 | (BIT_HEADSET | BIT_HEADSET_NO_MIC)) != 0) | ||
| 888 | remove_headset(); | ||
| 889 | #endif | ||
| 890 | |||
| 891 | unregister_common_headset(hi); | ||
| 892 | input_unregister_device(hi->input); | ||
| 893 | destroy_workqueue(detect_wq); | ||
| 894 | destroy_workqueue(button_wq); | ||
| 895 | switch_dev_unregister(&hi->sdev); | ||
| 896 | |||
| 897 | return 0; | ||
| 898 | } | ||
| 899 | |||
| 900 | static struct platform_driver htc_35mm_driver = { | ||
| 901 | .probe = htc_35mm_probe, | ||
| 902 | .remove = htc_35mm_remove, | ||
| 903 | .driver = { | ||
| 904 | .name = "HTC_HEADSET_MGR", | ||
| 905 | .owner = THIS_MODULE, | ||
| 906 | }, | ||
| 907 | }; | ||
| 908 | |||
| 909 | |||
| 910 | static int __init htc_35mm_init(void) | ||
| 911 | { | ||
| 912 | H2W_DBG(""); | ||
| 913 | return platform_driver_register(&htc_35mm_driver); | ||
| 914 | } | ||
| 915 | |||
| 916 | static void __exit htc_35mm_exit(void) | ||
| 917 | { | ||
| 918 | platform_driver_unregister(&htc_35mm_driver); | ||
| 919 | } | ||
| 920 | |||
| 921 | module_init(htc_35mm_init); | ||
| 922 | module_exit(htc_35mm_exit); | ||
| 923 | |||
| 924 | MODULE_DESCRIPTION("HTC headset manager driver"); | ||
| 925 | MODULE_LICENSE("GPL"); |
|   | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * /arch/arm/mach-msm/include/mach/htc_headset_gpio.h | ||
| 4 | * | ||
| 5 | * HTC GPIO headset detection driver. | ||
| 6 | * | ||
| 7 | * Copyright (C) 2010 HTC, Inc. | ||
| 8 | * | ||
| 9 | * This software is licensed under the terms of the GNU General Public | ||
| 10 | * License version 2, as published by the Free Software Foundation, and | ||
| 11 | * may be copied, distributed, and modified under those terms. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef HTC_HEADSET_GPIO_H | ||
| 21 | #define HTC_HEADSET_GPIO_H | ||
| 22 | |||
| 23 | struct htc_headset_gpio_platform_data { | ||
| 24 | unsigned int hpin_gpio; | ||
| 25 | unsigned int mic_detect_gpio; | ||
| 26 | unsigned int key_enable_gpio; | ||
| 27 | unsigned int mic_select_gpio; | ||
| 28 | unsigned int microp_channel; | ||
| 29 | }; | ||
| 30 | |||
| 31 | #endif |
|   | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * /arch/arm/mach-msm/include/mach/htc_headset_mgr.h | ||
| 4 | * | ||
| 5 | * HTC headset manager driver. | ||
| 6 | * | ||
| 7 | * Copyright (C) 2010 HTC, Inc. | ||
| 8 | * | ||
| 9 | * This software is licensed under the terms of the GNU General Public | ||
| 10 | * License version 2, as published by the Free Software Foundation, and | ||
| 11 | * may be copied, distributed, and modified under those terms. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef HTC_HEADSET_MGR_H | ||
| 21 | #define HTC_HEADSET_MGR_H | ||
| 22 | |||
| 23 | #include <mach/msm_rpcrouter.h> | ||
| 24 | |||
| 25 | #include <linux/switch.h> | ||
| 26 | #include <linux/input.h> | ||
| 27 | #include <linux/wakelock.h> | ||
| 28 | |||
| 29 | #define SYS_MSG(fmt, arg...) \ | ||
| 30 | printk(KERN_INFO "[" DRIVER_NAME "] (%s) " fmt "\n", __func__, ## arg) | ||
| 31 | #if 0 | ||
| 32 | #define DBG_MSG(fmt, arg...) \ | ||
| 33 | printk(KERN_INFO "##### [" DRIVER_NAME "] (%s) " fmt "\n", \ | ||
| 34 | __func__, ## arg) | ||
| 35 | #else | ||
| 36 | #define DBG_MSG(fmt, arg...) {} | ||
| 37 | #endif | ||
| 38 | |||
| 39 | #define DEVICE_ACCESSORY_ATTR(_name, _mode, _show, _store) \ | ||
| 40 | struct device_attribute dev_attr_##_name = \ | ||
| 41 | __ATTR(flag, _mode, _show, _store) | ||
| 42 | |||
| 43 | #define DRIVER_HS_MGR_RPC_SERVER (1 << 0) | ||
| 44 | |||
| 45 | #define HS_DEF_MIC_ADC_10_BIT 200 | ||
| 46 | #define HS_DEF_MIC_ADC_16_BIT 14894 /* (0.5 / 2.2) * (2 ^ 16) */ | ||
| 47 | |||
| 48 | #define HS_DELAY_ZERO 0 | ||
| 49 | #define HS_DELAY_MIC_BIAS 200 | ||
| 50 | #define HS_DELAY_MIC_DETECT 500 | ||
| 51 | #define HS_DELAY_INSERT 500 | ||
| 52 | #define HS_DELAY_REMOVE 200 | ||
| 53 | #define HS_DELAY_BUTTON 500 | ||
| 54 | |||
| 55 | #define HS_JIFFIES_ZERO msecs_to_jiffies(HS_DELAY_ZERO) | ||
| 56 | #define HS_JIFFIES_MIC_BIAS msecs_to_jiffies(HS_DELAY_MIC_BIAS) | ||
| 57 | #define HS_JIFFIES_MIC_DETECT msecs_to_jiffies(HS_DELAY_MIC_DETECT) | ||
| 58 | #define HS_JIFFIES_INSERT msecs_to_jiffies(HS_DELAY_INSERT) | ||
| 59 | #define HS_JIFFIES_REMOVE msecs_to_jiffies(HS_DELAY_REMOVE) | ||
| 60 | #define HS_JIFFIES_BUTTON msecs_to_jiffies(HS_DELAY_BUTTON) | ||
| 61 | |||
| 62 | /* Definitions for Headset RPC Server */ | ||
| 63 | #define HS_RPC_SERVER_PROG 0x30100004 | ||
| 64 | #define HS_RPC_SERVER_VERS 0x00000000 | ||
| 65 | #define HS_RPC_SERVER_PROC_NULL 0 | ||
| 66 | #define HS_RPC_SERVER_PROC_KEY 1 | ||
| 67 | |||
| 68 | /* Definitions for Headset RPC Client */ | ||
| 69 | #define HS_RPC_CLIENT_PROG 0x30100005 | ||
| 70 | #define HS_RPC_CLIENT_VERS 0x00000000 | ||
| 71 | #define HS_RPC_CLIENT_PROC_NULL 0 | ||
| 72 | #define HS_RPC_CLIENT_PROC_ADC 1 | ||
| 73 | |||
| 74 | #define HS_MGR_KEYCODE_END KEY_END /* 107 */ | ||
| 75 | #define HS_MGR_KEYCODE_MUTE KEY_MUTE /* 113 */ | ||
| 76 | #define HS_MGR_KEYCODE_VOLDOWN KEY_VOLUMEDOWN /* 114 */ | ||
| 77 | #define HS_MGR_KEYCODE_VOLUP KEY_VOLUMEUP /* 115 */ | ||
| 78 | #define HS_MGR_KEYCODE_FORWARD KEY_NEXTSONG /* 163 */ | ||
| 79 | #define HS_MGR_KEYCODE_PLAY KEY_PLAYPAUSE /* 164 */ | ||
| 80 | #define HS_MGR_KEYCODE_BACKWARD KEY_PREVIOUSSONG /* 165 */ | ||
| 81 | #define HS_MGR_KEYCODE_MEDIA KEY_MEDIA /* 226 */ | ||
| 82 | #define HS_MGR_KEYCODE_SEND KEY_SEND /* 231 */ | ||
| 83 | |||
| 84 | #define HEADSET_NO_MIC 0 | ||
| 85 | #define HEADSET_MIC 1 | ||
| 86 | #define HEADSET_METRICO 2 | ||
| 87 | |||
| 88 | #define HTC_35MM_UNPLUG 0 | ||
| 89 | #define HTC_35MM_NO_MIC 1 | ||
| 90 | #define HTC_35MM_MIC 2 | ||
| 91 | |||
| 92 | enum { | ||
| 93 | HEADSET_REG_REMOTE_ADC, | ||
| 94 | HEADSET_REG_RPC_KEY, | ||
| 95 | HEADSET_REG_MIC_STATUS, | ||
| 96 | HEADSET_REG_MIC_BIAS, | ||
| 97 | HEADSET_REG_MIC_SELECT, | ||
| 98 | HEADSET_REG_KEY_INT_ENABLE, | ||
| 99 | HEADSET_REG_KEY_ENABLE, | ||
| 100 | }; | ||
| 101 | |||
| 102 | enum { | ||
| 103 | HS_MGR_KEY_INVALID = -1, | ||
| 104 | HS_MGR_KEY_NONE = 0, | ||
| 105 | HS_MGR_KEY_PLAY = 1, | ||
| 106 | HS_MGR_KEY_BACKWARD = 2, | ||
| 107 | HS_MGR_KEY_FORWARD = 3, | ||
| 108 | }; | ||
| 109 | |||
| 110 | struct hs_rpc_server_args_key { | ||
| 111 | uint32_t adc; | ||
| 112 | }; | ||
| 113 | |||
| 114 | struct hs_rpc_client_req_adc { | ||
| 115 | struct rpc_request_hdr hdr; | ||
| 116 | }; | ||
| 117 | |||
| 118 | struct hs_rpc_client_rep_adc { | ||
| 119 | struct rpc_reply_hdr hdr; | ||
| 120 | uint32_t adc; | ||
| 121 | }; | ||
| 122 | |||
| 123 | struct headset_notifier { | ||
| 124 | int id; | ||
| 125 | void *func; | ||
| 126 | }; | ||
| 127 | |||
| 128 | struct hs_notifier_func { | ||
| 129 | int (*remote_adc)(int *); | ||
| 130 | void (*rpc_key)(int); | ||
| 131 | int (*mic_status)(void); | ||
| 132 | int (*mic_bias_enable)(int); | ||
| 133 | void (*mic_select)(int); | ||
| 134 | int (*key_int_enable)(int); | ||
| 135 | void (*key_enable)(int); | ||
| 136 | }; | ||
| 137 | |||
| 138 | struct htc_headset_mgr_platform_data { | ||
| 139 | unsigned int driver_flag; | ||
| 140 | |||
| 141 | int cable_in1; | ||
| 142 | int cable_in2; | ||
| 143 | int h2w_clk; | ||
| 144 | int h2w_data; | ||
| 145 | int debug_uart; | ||
| 146 | int headset_mic_35mm; | ||
| 147 | |||
| 148 | void (*h2w_power)(int); | ||
| 149 | void (*config)(int); | ||
| 150 | void (*set_dat)(int); | ||
| 151 | void (*set_clk)(int); | ||
| 152 | void (*set_dat_dir)(int); | ||
| 153 | void (*set_clk_dir)(int); | ||
| 154 | int (*get_dat)(void); | ||
| 155 | int (*get_clk)(void); | ||
| 156 | }; | ||
| 157 | |||
| 158 | #define BIT_HEADSET (1 << 0) | ||
| 159 | #define BIT_HEADSET_NO_MIC (1 << 1) | ||
| 160 | #define BIT_TTY_FULL (1 << 2) | ||
| 161 | #define BIT_FM_HEADSET (1 << 3) | ||
| 162 | #define BIT_FM_SPEAKER (1 << 4) | ||
| 163 | #define BIT_TTY_VCO (1 << 5) | ||
| 164 | #define BIT_TTY_HCO (1 << 6) | ||
| 165 | #define BIT_35MM_HEADSET (1 << 7) | ||
| 166 | #define BIT_TV_OUT (1 << 8) | ||
| 167 | #define BIT_USB_CRADLE (1 << 9) | ||
| 168 | #define BIT_TV_OUT_AUDIO (1 << 10) | ||
| 169 | #define BIT_HDMI_CABLE (1 << 11) | ||
| 170 | #define BIT_HDMI_AUDIO (1 << 12) | ||
| 171 | #define BIT_USB_HEADSET (1 << 13) | ||
| 172 | |||
| 173 | enum { | ||
| 174 | STATUS_DISCONNECTED = 0, | ||
| 175 | STATUS_CONNECTED_ENABLED = 1, | ||
| 176 | STATUS_CONNECTED_DISABLED = 2, | ||
| 177 | }; | ||
| 178 | |||
| 179 | enum { | ||
| 180 | H2W_GPIO = 0, | ||
| 181 | H2W_UART1 = 1, | ||
| 182 | H2W_UART3 = 2, | ||
| 183 | H2W_BT = 3 | ||
| 184 | }; | ||
| 185 | |||
| 186 | enum { | ||
| 187 | NO_DEVICE = 0, | ||
| 188 | HTC_HEADSET = 1, | ||
| 189 | NORMAL_HEARPHONE = 2, | ||
| 190 | H2W_DEVICE = 3, | ||
| 191 | USB_CRADLE = 4, | ||
| 192 | UART_DEBUG = 5, | ||
| 193 | H2W_TVOUT = 6, | ||
| 194 | USB_HEADSET = 7, | ||
| 195 | }; | ||
| 196 | |||
| 197 | #define RESEND_DELAY (3) /* ms */ | ||
| 198 | #define MAX_ACK_RESEND_TIMES (6) /* follow spec */ | ||
| 199 | #define MAX_HOST_RESEND_TIMES (3) /* follow spec */ | ||
| 200 | #define MAX_HYGEIA_RESEND_TIMES (5) | ||
| 201 | |||
| 202 | #define H2W_ASCR_DEVICE_INI (0x01) | ||
| 203 | #define H2W_ASCR_ACT_EN (0x02) | ||
| 204 | #define H2W_ASCR_PHONE_IN (0x04) | ||
| 205 | #define H2W_ASCR_RESET (0x08) | ||
| 206 | #define H2W_ASCR_AUDIO_IN (0x10) | ||
| 207 | |||
| 208 | #define H2W_LED_OFF (0x0) | ||
| 209 | #define H2W_LED_BKL (0x1) | ||
| 210 | #define H2W_LED_MTL (0x2) | ||
| 211 | |||
| 212 | #define H2W_PhoneIn (0x01) | ||
| 213 | #define H2W_MuteLed (0x02) | ||
| 214 | |||
| 215 | typedef enum { | ||
| 216 | /* === system group 0x0000~0x00FF === */ | ||
| 217 | /* (R) Accessory type register */ | ||
| 218 | H2W_SYSTEM = 0x0000, | ||
| 219 | /* (R) Maximum group address */ | ||
| 220 | H2W_MAX_GP_ADD = 0x0001, | ||
| 221 | /* (R/W) Accessory system control register0 */ | ||
| 222 | H2W_ASCR0 = 0x0002, | ||
| 223 | |||
| 224 | /* === key group 0x0100~0x01FF === */ | ||
| 225 | /* (R) Key group maximum sub address */ | ||
| 226 | H2W_KEY_MAXADD = 0x0100, | ||
| 227 | /* (R) ASCII key press down flag */ | ||
| 228 | H2W_ASCII_DOWN = 0x0101, | ||
| 229 | /* (R) ASCII key release up flag */ | ||
| 230 | H2W_ASCII_UP = 0x0102, | ||
| 231 | /* (R) Function key status flag */ | ||
| 232 | H2W_FNKEY_UPDOWN = 0x0103, | ||
| 233 | /* (R/W) Key device status */ | ||
| 234 | H2W_KD_STATUS = 0x0104, | ||
| 235 | |||
| 236 | /* === led group 0x0200~0x02FF === */ | ||
| 237 | /* (R) LED group maximum sub address */ | ||
| 238 | H2W_LED_MAXADD = 0x0200, | ||
| 239 | /* (R/W) LED control register0 */ | ||
| 240 | H2W_LEDCT0 = 0x0201, | ||
| 241 | |||
| 242 | /* === crdl group 0x0300~0x03FF === */ | ||
| 243 | /* (R) Cardle group maximum sub address */ | ||
| 244 | H2W_CRDL_MAXADD = 0x0300, | ||
| 245 | /* (R/W) Cardle group function control register0 */ | ||
| 246 | H2W_CRDLCT0 = 0x0301, | ||
| 247 | |||
| 248 | /* === car kit group 0x0400~0x04FF === */ | ||
| 249 | H2W_CARKIT_MAXADD = 0x0400, | ||
| 250 | |||
| 251 | /* === usb host group 0x0500~0x05FF === */ | ||
| 252 | H2W_USBHOST_MAXADD = 0x0500, | ||
| 253 | |||
| 254 | /* === medical group 0x0600~0x06FF === */ | ||
| 255 | H2W_MED_MAXADD = 0x0600, | ||
| 256 | H2W_MED_CONTROL = 0x0601, | ||
| 257 | H2W_MED_IN_DATA = 0x0602, | ||
| 258 | } H2W_ADDR; | ||
| 259 | |||
| 260 | typedef struct H2W_INFO { | ||
| 261 | /* system group */ | ||
| 262 | unsigned char CLK_SP; | ||
| 263 | int SLEEP_PR; | ||
| 264 | unsigned char HW_REV; | ||
| 265 | int AUDIO_DEVICE; | ||
| 266 | unsigned char ACC_CLASS; | ||
| 267 | unsigned char MAX_GP_ADD; | ||
| 268 | |||
| 269 | /* key group */ | ||
| 270 | int KEY_MAXADD; | ||
| 271 | int ASCII_DOWN; | ||
| 272 | int ASCII_UP; | ||
| 273 | int FNKEY_UPDOWN; | ||
| 274 | int KD_STATUS; | ||
| 275 | |||
| 276 | /* led group */ | ||
| 277 | int LED_MAXADD; | ||
| 278 | int LEDCT0; | ||
| 279 | |||
| 280 | /* medical group */ | ||
| 281 | int MED_MAXADD; | ||
| 282 | unsigned char AP_ID; | ||
| 283 | unsigned char AP_EN; | ||
| 284 | unsigned char DATA_EN; | ||
| 285 | } H2W_INFO; | ||
| 286 | |||
| 287 | typedef enum { | ||
| 288 | H2W_500KHz = 1, | ||
| 289 | H2W_250KHz = 2, | ||
| 290 | H2W_166KHz = 3, | ||
| 291 | H2W_125KHz = 4, | ||
| 292 | H2W_100KHz = 5, | ||
| 293 | H2W_83KHz = 6, | ||
| 294 | H2W_71KHz = 7, | ||
| 295 | H2W_62KHz = 8, | ||
| 296 | H2W_55KHz = 9, | ||
| 297 | H2W_50KHz = 10, | ||
| 298 | } H2W_SPEED; | ||
| 299 | |||
| 300 | struct h2w_info { | ||
| 301 | unsigned int driver_flag; | ||
| 302 | |||
| 303 | unsigned long hpin_jiffies; | ||
| 304 | |||
| 305 | struct class *htc_accessory_class; | ||
| 306 | struct device *tty_dev; | ||
| 307 | struct device *fm_dev; | ||
| 308 | struct device *mic_dev; | ||
| 309 | struct device *mute_dev; | ||
| 310 | struct device *phonein_dev; | ||
| 311 | struct mutex mutex_lock; | ||
| 312 | struct mutex mutex_rc_lock; | ||
| 313 | |||
| 314 | struct switch_dev sdev; | ||
| 315 | struct input_dev *input; | ||
| 316 | unsigned long insert_jiffies; | ||
| 317 | |||
| 318 | int ignore_btn; | ||
| 319 | atomic_t btn_state; | ||
| 320 | |||
| 321 | int tty_enable_flag; | ||
| 322 | int fm_flag; | ||
| 323 | int mic_switch_flag; | ||
| 324 | int rc_flag; | ||
| 325 | |||
| 326 | unsigned int irq; | ||
| 327 | unsigned int irq_btn; | ||
| 328 | unsigned int irq_btn_35mm; | ||
| 329 | |||
| 330 | int cable_in1; | ||
| 331 | int cable_in2; | ||
| 332 | int h2w_clk; | ||
| 333 | int h2w_data; | ||
| 334 | int debug_uart; | ||
| 335 | int headset_mic_35mm; | ||
| 336 | |||
| 337 | /* The variables were used by 35mm headset*/ | ||
| 338 | int key_level_flag; | ||
| 339 | int ext_35mm_status; | ||
| 340 | int h2w_35mm_status; | ||
| 341 | int is_ext_insert; | ||
| 342 | int mic_bias_state; | ||
| 343 | int metrico_status; /* For HW Metrico lab test */ | ||
| 344 | |||
| 345 | /* The variables are used by USB headset */ | ||
| 346 | int usb_dev_type; | ||
| 347 | int usb_dev_status; | ||
| 348 | |||
| 349 | void (*insert_11pin_35mm)(int *); | ||
| 350 | void (*remove_11pin_35mm)(void); | ||
| 351 | |||
| 352 | void (*configure) (int); | ||
| 353 | int (*get_path) (void); | ||
| 354 | void (*h2w_power)(int); | ||
| 355 | void (*set_dat)(int); | ||
| 356 | void (*set_clk)(int); | ||
| 357 | void (*set_dat_dir)(int); | ||
| 358 | void (*set_clk_dir)(int); | ||
| 359 | int (*get_dat)(void); | ||
| 360 | int (*get_clk)(void); | ||
| 361 | |||
| 362 | H2W_INFO h2w_info; | ||
| 363 | H2W_SPEED speed; | ||
| 364 | |||
| 365 | struct wake_lock headset_wake_lock; | ||
| 366 | }; | ||
| 367 | |||
| 368 | int headset_notifier_register(struct headset_notifier *notifier); | ||
| 369 | |||
| 370 | void insert_headset(int); | ||
| 371 | void remove_headset(void); | ||
| 372 | |||
| 373 | void headset_button_event(int is_press, int type); | ||
| 374 | void button_pressed(int type); | ||
| 375 | void button_released(int type); | ||
| 376 | |||
| 377 | void button_h2w_do_work(struct work_struct *w); | ||
| 378 | void detect_h2w_do_work(struct work_struct *w); | ||
| 379 | |||
| 380 | void headset_ext_detect(int type); | ||
| 381 | |||
| 382 | extern int switch_send_event(unsigned int bit, int on); | ||
| 383 | |||
| 384 | /* notify the 3.5mm driver of events */ | ||
| 385 | int htc_35mm_remote_notify_ext_headset_irq(int insert); | ||
| 386 | int htc_35mm_remote_notify_insert_ext_headset(int insert); | ||
| 387 | int htc_35mm_remote_notify_microp_ready(void); | ||
| 388 | int htc_35mm_remote_notify_button_status(int key_level); | ||
| 389 | int htc_35mm_remote_notify_irq_enable(int enable); | ||
| 390 | |||
| 391 | void hs_notify_hpin_irq(void); | ||
| 392 | int hs_hpin_stable(void); | ||
| 393 | |||
| 394 | #endif |

