static void msg_ind_set_blink(struct msg_ind_led_data *msg_ind_data, bool enable) { unsigned short led_reg; unsigned int i; if (enable) { if (!(msg_ind_data->regulator_state&LD_LED_BLINK)) { cpcap_uc_start(msg_ind_data->cpcap, CPCAP_BANK_PRIMARY, CPCAP_MACRO_6); printk_cpcap("%s: cpcap_uc_start(CPCAP_MACRO_6)\n", __func__); if (msg_ind_data->cpcap_leds->rgb_led.regulator_macro_controlled) { /* Wait for the macro to start. */ cpcap_regacc_read(msg_ind_data->cpcap, CPCAP_REG_REDC, &led_reg); i = 21; while ((!(led_reg & LD_MSG_IND_CPCAP_BLINK_ON)) && (--i > 0)) { cpcap_uc_start(msg_ind_data->cpcap, CPCAP_BANK_PRIMARY, CPCAP_MACRO_6); msleep(10); cpcap_regacc_read(msg_ind_data->cpcap, CPCAP_REG_REDC, &led_reg); } if (i == 0) printk(KERN_ERR "%s: Unable to sync CPCAP blink on macro.\n", __func__); printk_cpcap("%s: Blink macro started.\n", __func__); /* Shutdown the regulator since the macro handles the regulator. */ msg_ind_set_regulator(msg_ind_data, false, true); } msg_ind_data->regulator_state |= LD_LED_BLINK; } } else { if (msg_ind_data->regulator_state&LD_LED_BLINK) { cpcap_uc_stop(msg_ind_data->cpcap, CPCAP_BANK_PRIMARY, CPCAP_MACRO_6); printk_cpcap("%s: cpcap_uc_stop(CPCAP_MACRO_6)\n", __func__); if (msg_ind_data->cpcap_leds->rgb_led.regulator_macro_controlled) { /* Wait for the macro to be stopped before moving on. */ cpcap_regacc_read(msg_ind_data->cpcap, CPCAP_REG_REDC, &led_reg); i = 21; while ((led_reg & LD_MSG_IND_CPCAP_BLINK_ON) && (--i > 0)) { cpcap_uc_stop(msg_ind_data->cpcap, CPCAP_BANK_PRIMARY, CPCAP_MACRO_6); msleep(10); cpcap_regacc_read(msg_ind_data->cpcap, CPCAP_REG_REDC, &led_reg); } if (i == 0) printk(KERN_ERR "%s: Unable to sync CPCAP blink off macro.\n", __func__); printk_cpcap("%s: Blink macro stopped.\n", __func__); /* If any LED's are on re-enable the regulator. */ if (msg_ind_data->regulator_state&(LD_LED_RED|LD_LED_GREEN|LD_LED_BLUE)) msg_ind_set_regulator(msg_ind_data, true, false); } msg_ind_data->regulator_state &= ~LD_LED_BLINK; } } }
static void key_handler(enum cpcap_irqs irq, void *data) { struct cpcap_3mm5_data *data_3mm5 = data; if ((irq != CPCAP_IRQ_MB2) && (irq != CPCAP_IRQ_UC_PRIMACRO_5)) return; if ((cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_HS, 1) == 1) || (switch_get_state(&data_3mm5->sdev) != HEADSET_WITH_MIC)) { hs_handler(CPCAP_IRQ_HS, data_3mm5); return; } if ((cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_MB2, 0) == 0) || (cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_PTT, 0) == 0)) { send_key_event(data_3mm5, 1); /* If macro not available, only short presses are supported */ if (!cpcap_uc_status(data_3mm5->cpcap, CPCAP_MACRO_5)) { send_key_event(data_3mm5, 0); /* Attempt to restart the macro for next time. */ cpcap_uc_start(data_3mm5->cpcap, CPCAP_MACRO_5); } } else send_key_event(data_3mm5, 0); cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_MB2); cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); }
static int __init cpcap_3mm5_probe(struct platform_device *pdev) { int retval = 0; struct cpcap_3mm5_data *data; if (pdev->dev.platform_data == NULL) { dev_err(&pdev->dev, "no platform_data\n"); return -EINVAL; } data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->cpcap = pdev->dev.platform_data; data->audio_low_pwr_det = 1; data->audio_low_pwr_mac13 = 1; data->sdev.name = "h2w"; data->sdev.print_name = print_name; switch_dev_register(&data->sdev); INIT_DELAYED_WORK(&data->work, mac13_work); platform_set_drvdata(pdev, data); data->regulator = regulator_get(NULL, "vaudio"); if (IS_ERR(data->regulator)) { dev_err(&pdev->dev, "Could not get regulator for cpcap_3mm5\n"); retval = PTR_ERR(data->regulator); goto free_mem; } regulator_set_voltage(data->regulator, 2775000, 2775000); /* configure GPIO2=high and GPIO4=low. */ cpcap_regacc_write(data->cpcap, CPCAP_REG_GPIO2, 0, CPCAP_BIT_GPIO2DRV); cpcap_regacc_write(data->cpcap, CPCAP_REG_GPIO4, 0, CPCAP_BIT_GPIO4DRV); retval = cpcap_irq_clear(data->cpcap, CPCAP_IRQ_HS); retval |= cpcap_irq_clear(data->cpcap, CPCAP_IRQ_MB2); retval |= cpcap_irq_clear(data->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); retval |= cpcap_irq_clear(data->cpcap, CPCAP_IRQ_UC_PRIMACRO_13); if (retval) goto reg_put; retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_HS, hs_handler, data); if (retval) goto reg_put; retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_MB2, key_handler, data); if (retval) goto free_hs; retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_UC_PRIMACRO_5, key_handler, data); if (retval) goto free_mb2; if (data->cpcap->vendor == CPCAP_VENDOR_ST) { retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_UC_PRIMACRO_13, mac13_handler, data); if (retval) goto free_mac5; cpcap_uc_start(data->cpcap, CPCAP_MACRO_13); } hs_handler(CPCAP_IRQ_HS, data); return 0; free_mac5: cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); free_mb2: cpcap_irq_free(data->cpcap, CPCAP_IRQ_MB2); free_hs: cpcap_irq_free(data->cpcap, CPCAP_IRQ_HS); reg_put: regulator_put(data->regulator); free_mem: kfree(data); return retval; }
static void hs_handler(enum cpcap_irqs irq, void *data) { struct cpcap_3mm5_data *data_3mm5 = data; int new_state = NO_DEVICE; if (irq != CPCAP_IRQ_HS) return; /* HS sense of 1 means no headset present, 0 means headset attached. */ if (cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_HS, 1) == 1) { cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_TXI, 0, (CPCAP_BIT_MB_ON2 | CPCAP_BIT_PTT_CMP_EN)); cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_RXOA, 0, CPCAP_BIT_ST_HS_CP_EN); audio_low_power_set(data_3mm5, &data_3mm5->audio_low_pwr_det); cpcap_irq_mask(data_3mm5->cpcap, CPCAP_IRQ_MB2); cpcap_irq_mask(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); cpcap_irq_clear(data_3mm5->cpcap, CPCAP_IRQ_MB2); cpcap_irq_clear(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_HS); send_key_event(data_3mm5, 0); /* Config mux switch to accy detection. */ cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_GPIO2, 0, CPCAP_BIT_GPIO2DRV); cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_GPIO4, 0, CPCAP_BIT_GPIO4DRV); cpcap_uc_stop(data_3mm5->cpcap, CPCAP_MACRO_5); } else { /* Config mux switch to pass headset mic. */ cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_GPIO2, 0, CPCAP_BIT_GPIO2DRV); cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_GPIO4, CPCAP_BIT_GPIO4DRV, CPCAP_BIT_GPIO4DRV); cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_TXI, (CPCAP_BIT_MB_ON2 | CPCAP_BIT_PTT_CMP_EN), (CPCAP_BIT_MB_ON2 | CPCAP_BIT_PTT_CMP_EN)); cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_RXOA, CPCAP_BIT_ST_HS_CP_EN, CPCAP_BIT_ST_HS_CP_EN); audio_low_power_clear(data_3mm5, &data_3mm5->audio_low_pwr_det); /* Give PTTS time to settle 10ms */ msleep(11); if (cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_PTT, 1) <= 0) { /* Headset without mic and MFB is detected. (May also * be a headset with the MFB pressed.) */ new_state = HEADSET_WITHOUT_MIC; } else { new_state = HEADSET_WITH_MIC; /* Config mux switch to pass headset mic. */ cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_GPIO2, 0, CPCAP_BIT_GPIO2DRV); cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_GPIO4, CPCAP_BIT_GPIO4DRV, CPCAP_BIT_GPIO4DRV); } cpcap_irq_clear(data_3mm5->cpcap, CPCAP_IRQ_MB2); cpcap_irq_clear(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_HS); cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_MB2); cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); cpcap_uc_start(data_3mm5->cpcap, CPCAP_MACRO_5); headset_in_stamp = jiffies; } switch_set_state(&data_3mm5->sdev, new_state); if (data_3mm5->cpcap->h2w_new_state) data_3mm5->cpcap->h2w_new_state(new_state); dev_info(&data_3mm5->cpcap->spi->dev, "New headset state: %d\n", new_state); }
static void hs_handler(enum cpcap_irqs irq, void *data) { struct cpcap_3mm5_data *data_3mm5 = data; int new_state = NO_DEVICE; if (irq != CPCAP_IRQ_HS) return; /* HS sense of 1 means no headset present, 0 means headset attached. */ if (cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_HS, 1) == 1) { cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_TXI, 0, (CPCAP_BIT_MB_ON2 | CPCAP_BIT_PTT_CMP_EN)); cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_RXOA, 0, CPCAP_BIT_ST_HS_CP_EN); audio_low_power_set(data_3mm5, &data_3mm5->audio_low_pwr_det); cpcap_irq_mask(data_3mm5->cpcap, CPCAP_IRQ_MB2); cpcap_irq_mask(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); cpcap_irq_clear(data_3mm5->cpcap, CPCAP_IRQ_MB2); cpcap_irq_clear(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_HS); send_key_event(data_3mm5, 0); data_3mm5->unsupported_headset=0; cpcap_uc_stop(data_3mm5->cpcap, CPCAP_BANK_PRIMARY, CPCAP_MACRO_5); } else { cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_TXI, (CPCAP_BIT_MB_ON2 | CPCAP_BIT_PTT_CMP_EN), (CPCAP_BIT_MB_ON2 | CPCAP_BIT_PTT_CMP_EN)); cpcap_regacc_write(data_3mm5->cpcap, CPCAP_REG_RXOA, CPCAP_BIT_ST_HS_CP_EN, CPCAP_BIT_ST_HS_CP_EN); audio_low_power_clear(data_3mm5, &data_3mm5->audio_low_pwr_det); /* Give PTTS time to settle */ mdelay(2); if (cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_PTT, 1) <= 0) { /* Headset without mic and MFB is detected. (May also * be a headset with the MFB pressed.) */ new_state = HEADSET_WITHOUT_MIC; } else { if(cpcap_irq_sense(data_3mm5->cpcap, CPCAP_IRQ_MB2, 1) == 0) { /*cvk011c: This is an Apple HS and its microphone will not work with CPCAP due to HW problem. Detect it as HS without mic to avoid problems with HS BTN detection and MIC audio */ new_state = HEADSET_WITHOUT_MIC; dev_info(&data_3mm5->cpcap->spi->dev, "Unsupported headset detected\n"); data_3mm5->unsupported_headset=1; }else new_state = HEADSET_WITH_MIC; } cpcap_irq_clear(data_3mm5->cpcap, CPCAP_IRQ_MB2); cpcap_irq_clear(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_HS); cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_MB2); cpcap_irq_unmask(data_3mm5->cpcap, CPCAP_IRQ_UC_PRIMACRO_5); cpcap_uc_start(data_3mm5->cpcap, CPCAP_BANK_PRIMARY, CPCAP_MACRO_5); } switch_set_state(&data_3mm5->sdev, new_state); if (data_3mm5->cpcap->h2w_new_state) data_3mm5->cpcap->h2w_new_state(new_state); dev_info(&data_3mm5->cpcap->spi->dev, "New headset state: %d\n", new_state); }