From: Praveen Bharathi Date: Thu, 14 Oct 2010 17:38:36 +0000 (-0500) Subject: [ARM] mfd: cpcap-audio: add support for basic analog dock X-Git-Tag: firefly_0821_release~9834^2~375 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=a5ee9f2338e7c3faf5f1c759ca5a4cdd8da6fce1;p=firefly-linux-kernel-4.4.55.git [ARM] mfd: cpcap-audio: add support for basic analog dock The audio will now be routed to dock accesory when an accesory connected to dock is detected. Signed-off-by: Praveen Bharathi Signed-off-by: Iliyan Malchev --- diff --git a/drivers/mfd/cpcap-audio-core.c b/drivers/mfd/cpcap-audio-core.c index c3027cdef21e..143c2592bbaa 100644 --- a/drivers/mfd/cpcap-audio-core.c +++ b/drivers/mfd/cpcap-audio-core.c @@ -146,6 +146,22 @@ static inline bool is_output_headset(struct cpcap_audio_state *state) return false; } +static inline int is_output_whisper_emu(struct cpcap_audio_state *state) +{ + if (state->codec_primary_speaker == CPCAP_AUDIO_OUT_EMU_STEREO + || state->codec_secondary_speaker == CPCAP_AUDIO_OUT_EMU_STEREO + || state->stdac_primary_speaker == CPCAP_AUDIO_OUT_EMU_STEREO + || state->stdac_secondary_speaker == CPCAP_AUDIO_OUT_EMU_STEREO + || state->ext_primary_speaker == CPCAP_AUDIO_OUT_EMU_STEREO + || state->ext_secondary_speaker == CPCAP_AUDIO_OUT_EMU_STEREO) { + pr_debug("%s() returning TRUE\n", __func__); + return 1; + } + + pr_debug("%s() returning FALSE\n", __func__); + return 0; +} + static inline bool is_speaker_turning_off(struct cpcap_audio_state *state, struct cpcap_audio_state *prev) { @@ -248,6 +264,13 @@ static unsigned short int cpcap_audio_get_codec_output_amp_switches( value |= CPCAP_BIT_ALEFT_HS_CDC_SW; break; + case CPCAP_AUDIO_OUT_EMU_STEREO: + if (balance != CPCAP_AUDIO_BALANCE_R_ONLY) + value |= CPCAP_BIT_PGA_OUTR_USBDP_CDC_SW; + if (balance != CPCAP_AUDIO_BALANCE_L_ONLY) + value |= CPCAP_BIT_PGA_OUTL_USBDN_CDC_SW; + break; + case CPCAP_AUDIO_OUT_LINEOUT: value |= CPCAP_BIT_A4_LINEOUT_R_CDC_SW | CPCAP_BIT_A4_LINEOUT_L_CDC_SW; @@ -285,6 +308,13 @@ static unsigned short int cpcap_audio_get_stdac_output_amp_switches( value |= CPCAP_BIT_ARIGHT_HS_DAC_SW; break; + case CPCAP_AUDIO_OUT_EMU_STEREO: + if (balance != CPCAP_AUDIO_BALANCE_R_ONLY) + value |= CPCAP_BIT_PGA_OUTR_USBDP_DAC_SW; + if (balance != CPCAP_AUDIO_BALANCE_L_ONLY) + value |= CPCAP_BIT_PGA_OUTL_USBDN_DAC_SW; + break; + case CPCAP_AUDIO_OUT_LOUDSPEAKER: value |= CPCAP_BIT_A2_LDSP_L_DAC_SW | CPCAP_BIT_MONO_DAC0 | CPCAP_BIT_MONO_DAC1; @@ -327,6 +357,13 @@ static unsigned short int cpcap_audio_get_ext_output_amp_switches( CPCAP_BIT_PGA_EXT_L_EN; break; + case CPCAP_AUDIO_OUT_EMU_STEREO: + if (balance != CPCAP_AUDIO_BALANCE_R_ONLY) + value |= CPCAP_BIT_PGA_OUTR_USBDP_EXT_SW; + if (balance != CPCAP_AUDIO_BALANCE_L_ONLY) + value |= CPCAP_BIT_PGA_OUTL_USBDN_EXT_SW; + break; + case CPCAP_AUDIO_OUT_LOUDSPEAKER: value = CPCAP_BIT_A2_LDSP_L_EXT_SW | CPCAP_BIT_PGA_EXT_L_EN; break; @@ -413,9 +450,6 @@ static void cpcap_audio_set_output_amp_switches(struct cpcap_audio_state *state) static bool cpcap_audio_set_bits_for_speaker(int speaker, int balance, unsigned short int *message) { - pr_debug("%s() called with speaker = %d\n", __func__, - speaker); - /* Get the data required to enable each possible path */ switch (speaker) { case CPCAP_AUDIO_OUT_HANDSET: @@ -430,6 +464,13 @@ static bool cpcap_audio_set_bits_for_speaker(int speaker, int balance, (*message) |= CPCAP_BIT_HS_R_EN; break; + case CPCAP_AUDIO_OUT_EMU_STEREO: + if (balance != CPCAP_AUDIO_BALANCE_R_ONLY) + (*message) |= CPCAP_BIT_EMU_SPKR_R_EN; + if (balance != CPCAP_AUDIO_BALANCE_L_ONLY) + (*message) |= CPCAP_BIT_EMU_SPKR_L_EN; + break; + case CPCAP_AUDIO_OUT_LOUDSPEAKER: (*message) |= CPCAP_BIT_A2_LDSP_L_EN; break; @@ -969,6 +1010,20 @@ static void cpcap_audio_configure_power(int power) previous_power = power; } +static void cpcap_activate_whisper_emu_audio(struct cpcap_audio_state *state) +{ + struct cpcap_regacc reg_changes; + + pr_debug("%s() called\n", __func__); + + if (is_output_whisper_emu(state)) { + reg_changes.mask |= CPCAP_BIT_DIG_AUD_IN; + reg_changes.value = 0; + logged_cpcap_write(state->cpcap, CPCAP_REG_CDI, + reg_changes.value, reg_changes.mask); + } +} + void cpcap_audio_state_dump(struct cpcap_audio_state *state) { pr_info("mode = %d", state->mode); @@ -1026,6 +1081,8 @@ void cpcap_audio_register_dump(struct cpcap_audio_state *state) printk(KERN_INFO "0x20C[524] = %x\n", reg_val); cpcap_regacc_read(state->cpcap, CPCAP_REG_A2LA, ®_val); printk(KERN_INFO "0x20D[525] = %x\n", reg_val); + cpcap_regacc_read(state->cpcap, CPCAP_REG_USBC2, ®_val); + printk(KERN_INFO "0x381[897] = %x\n", reg_val); } static inline bool should_power_on(struct cpcap_audio_state *state) @@ -1117,6 +1174,8 @@ void cpcap_audio_set_audio_state(struct cpcap_audio_state *state) cpcap_audio_configure_aud_mute(state, prev); + cpcap_activate_whisper_emu_audio(state); + if (!power_on) cpcap_audio_configure_power(0); diff --git a/drivers/mfd/cpcap-core.c b/drivers/mfd/cpcap-core.c index da12ce2b063e..c56d688b0509 100644 --- a/drivers/mfd/cpcap-core.c +++ b/drivers/mfd/cpcap-core.c @@ -429,8 +429,8 @@ static long accy_ioctl(unsigned int cmd, unsigned long arg) sizeof(read_data))) return -EFAULT; read_data.dock_id[CPCAP_WHISPER_ID_SIZE - 1] = '\0'; - retval = cpcap_accy_whisper(misc_cpcap, read_data.cmd, - read_data.dock_id); + read_data.dock_prop[CPCAP_WHISPER_PROP_SIZE - 1] = '\0'; + retval = cpcap_accy_whisper(misc_cpcap, &read_data); break; default: diff --git a/drivers/mfd/cpcap-whisper.c b/drivers/mfd/cpcap-whisper.c index 0d7dae5e6245..a5a46e43743d 100644 --- a/drivers/mfd/cpcap-whisper.c +++ b/drivers/mfd/cpcap-whisper.c @@ -97,8 +97,8 @@ enum { NO_DOCK, DESK_DOCK, CAR_DOCK, - MOBILE_DOCK, - HD_DOCK, + LE_DOCK, + HE_DOCK, }; struct cpcap_whisper_data { @@ -117,6 +117,7 @@ struct cpcap_whisper_data { struct switch_dev asdev; /* Audio switch */ struct switch_dev csdev; /* Invalid charger switch */ char dock_id[CPCAP_WHISPER_ID_SIZE]; + char dock_prop[CPCAP_WHISPER_PROP_SIZE]; struct otg_transceiver *otg; }; @@ -134,10 +135,10 @@ static ssize_t print_name(struct switch_dev *dsdev, char *buf) return sprintf(buf, "DESK\n"); case CAR_DOCK: return sprintf(buf, "CAR\n"); - case MOBILE_DOCK: - return sprintf(buf, "MOBILE\n"); - case HD_DOCK: - return sprintf(buf, "HD\n"); + case LE_DOCK: + return sprintf(buf, "LE\n"); + case HE_DOCK: + return sprintf(buf, "HE\n"); } return -EINVAL; @@ -154,6 +155,17 @@ static ssize_t dock_id_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(dock_addr, S_IRUGO | S_IWUSR, dock_id_show, NULL); +static ssize_t dock_prop_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct switch_dev *dsdev = dev_get_drvdata(dev); + struct cpcap_whisper_data *data = + container_of(dsdev, struct cpcap_whisper_data, dsdev); + + return snprintf(buf, PAGE_SIZE, "%s\n", data->dock_prop); +} +static DEVICE_ATTR(dock_prop, S_IRUGO | S_IWUSR, dock_prop_show, NULL); + static void vusb_enable(struct cpcap_whisper_data *data) { if (!data->is_vusb_enabled) { @@ -323,6 +335,7 @@ static void whisper_notify(struct cpcap_whisper_data *di, enum cpcap_accy accy) switch_set_state(&di->asdev, 0); switch_set_state(&di->csdev, 0); memset(di->dock_id, 0, CPCAP_WHISPER_ID_SIZE); + memset(di->dock_prop, 0, CPCAP_WHISPER_PROP_SIZE); } } @@ -385,7 +398,8 @@ static void whisper_det_work(struct work_struct *work) case SAMPLE_1: get_sense(data); data->state = SAMPLE_2; - queue_delayed_work(data->wq, &data->work, msecs_to_jiffies(100)); + queue_delayed_work(data->wq, &data->work, + msecs_to_jiffies(100)); break; case SAMPLE_2: @@ -398,7 +412,8 @@ static void whisper_det_work(struct work_struct *work) } else data->state = IDENTIFY; - queue_delayed_work(data->wq, &data->work, msecs_to_jiffies(100)); + queue_delayed_work(data->wq, &data->work, + msecs_to_jiffies(100)); break; case IDENTIFY: @@ -418,7 +433,8 @@ static void whisper_det_work(struct work_struct *work) /* Special handling of USB undetect. */ data->state = USB; - } else if ((data->sense & SENSE_USB_HOST_MASK) == SENSE_USB_HOST) { + } else if ((data->sense & SENSE_USB_HOST_MASK) == + SENSE_USB_HOST) { whisper_notify(data, CPCAP_ACCY_USB_HOST); data->state = USB_POWER; @@ -555,8 +571,8 @@ static void whisper_int_handler(enum cpcap_irqs int_event, void *data) queue_delayed_work(di->wq, &(di->work), 0); } -int cpcap_accy_whisper(struct cpcap_device *cpcap, unsigned int cmd, - char *dock_id) +int cpcap_accy_whisper(struct cpcap_device *cpcap, + struct cpcap_whisper_request *req) { struct cpcap_whisper_data *di = cpcap->accydata; int retval = -EAGAIN; @@ -569,14 +585,15 @@ int cpcap_accy_whisper(struct cpcap_device *cpcap, unsigned int cmd, /* Can only change settings if not debouncing and whisper device * is present. */ if (di->state == WHISPER) { - if (cmd & CPCAP_WHISPER_ENABLE_UART) + if (req->cmd & CPCAP_WHISPER_ENABLE_UART) value = CPCAP_BIT_EMUMODE0; retval = cpcap_regacc_write(cpcap, CPCAP_REG_USBC2, value, (CPCAP_BIT_EMUMODE2 | CPCAP_BIT_EMUMODE1 | CPCAP_BIT_EMUMODE0)); - value = (cmd & CPCAP_WHISPER_MODE_PU) ? CPCAP_BIT_ID100KPU : 0; + value = (req->cmd & CPCAP_WHISPER_MODE_PU) ? + CPCAP_BIT_ID100KPU : 0; retval |= cpcap_regacc_write(cpcap, CPCAP_REG_USBC1, value, CPCAP_BIT_ID100KPU); if (value) { @@ -589,19 +606,27 @@ int cpcap_accy_whisper(struct cpcap_device *cpcap, unsigned int cmd, } /* Report dock type to system. */ - dock = (cmd & CPCAP_WHISPER_ACCY_MASK) >> + dock = (req->cmd & CPCAP_WHISPER_ACCY_MASK) >> CPCAP_WHISPER_ACCY_SHFT; - if (dock && (strlen(dock_id) < CPCAP_WHISPER_ID_SIZE)) - strncpy(di->dock_id, dock_id, CPCAP_WHISPER_ID_SIZE); + if (dock && (strlen(req->dock_id) < CPCAP_WHISPER_ID_SIZE)) + strncpy(di->dock_id, req->dock_id, + CPCAP_WHISPER_ID_SIZE); + if (dock && (strlen(req->dock_prop) < CPCAP_WHISPER_PROP_SIZE)) + strncpy(di->dock_prop, req->dock_prop, + CPCAP_WHISPER_PROP_SIZE); switch_set_state(&di->dsdev, dock); whisper_audio_check(di); } else if (di->state == WHISPER_SMART) { /* Report dock type to system. */ - dock = (cmd & CPCAP_WHISPER_ACCY_MASK) >> + dock = (req->cmd & CPCAP_WHISPER_ACCY_MASK) >> CPCAP_WHISPER_ACCY_SHFT; - if (dock && (strlen(dock_id) < CPCAP_WHISPER_ID_SIZE)) - strncpy(di->dock_id, dock_id, CPCAP_WHISPER_ID_SIZE); + if (dock && (strlen(req->dock_id) < CPCAP_WHISPER_ID_SIZE)) + strncpy(di->dock_id, req->dock_id, + CPCAP_WHISPER_ID_SIZE); + if (dock && (strlen(req->dock_prop) < CPCAP_WHISPER_PROP_SIZE)) + strncpy(di->dock_prop, req->dock_prop, + CPCAP_WHISPER_PROP_SIZE); switch_set_state(&di->dsdev, dock); retval = 0; } @@ -617,7 +642,7 @@ void cpcap_accy_whisper_spdif_set_state(int state) if (!switch_get_state(&whisper_di->dsdev)) return; - state = ((state > 0) ? 1 : 0); + state = ((state > 0) ? 2 : 0); switch_set_state(&whisper_di->asdev, state); pr_info("%s: Audio cable %s present\n", __func__, @@ -659,9 +684,14 @@ static int cpcap_whisper_probe(struct platform_device *pdev) switch_dev_register(&data->dsdev); retval = device_create_file(data->dsdev.dev, &dev_attr_dock_addr); if (retval < 0) { - dev_err(&pdev->dev, "Failed to create device file\n"); + dev_err(&pdev->dev, "Failed to create dock_addr file\n"); goto free_mem; } + retval = device_create_file(data->dsdev.dev, &dev_attr_dock_prop); + if (retval < 0) { + dev_err(&pdev->dev, "Failed to create dock_prop file\n"); + goto free_dock_id; + } platform_set_drvdata(pdev, data); @@ -670,7 +700,7 @@ static int cpcap_whisper_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Could not get regulator for cpcap_whisper\n"); retval = PTR_ERR(data->regulator); - goto free_dock_id; + goto free_dock_prop; } regulator_set_voltage(data->regulator, 3300000, 3300000); @@ -713,6 +743,8 @@ free_irqs: cpcap_irq_free(data->cpcap, CPCAP_IRQ_IDFLOAT); cpcap_irq_free(data->cpcap, CPCAP_IRQ_CHRG_DET); regulator_put(data->regulator); +free_dock_prop: + device_remove_file(data->dsdev.dev, &dev_attr_dock_prop); free_dock_id: device_remove_file(data->dsdev.dev, &dev_attr_dock_addr); free_mem: @@ -738,6 +770,7 @@ static int __exit cpcap_whisper_remove(struct platform_device *pdev) cancel_delayed_work_sync(&data->work); destroy_workqueue(data->wq); + device_remove_file(data->dsdev.dev, &dev_attr_dock_prop); device_remove_file(data->dsdev.dev, &dev_attr_dock_addr); switch_dev_unregister(&data->wsdev); switch_dev_unregister(&data->dsdev); diff --git a/drivers/mfd/tegra-cpcap-audio.c b/drivers/mfd/tegra-cpcap-audio.c index 77249dc083d8..45e596a81fa9 100644 --- a/drivers/mfd/tegra-cpcap-audio.c +++ b/drivers/mfd/tegra-cpcap-audio.c @@ -119,6 +119,14 @@ static void tegra_setup_audio_out_headset_and_speaker_on(void) gpio_direction_output(pdata->headset_gpio, 1); } +static void tegra_setup_audio_out_dock_headset_on(void) +{ + pdata->state->stdac_mode = CPCAP_AUDIO_STDAC_ON; + pdata->state->stdac_primary_speaker = CPCAP_AUDIO_OUT_EMU_STEREO; + pdata->state->stdac_secondary_speaker = CPCAP_AUDIO_OUT_EMU_STEREO; + cpcap_audio_set_audio_state(pdata->state); +} + static void tegra_setup_audio_in_mute(void) { pdata->state->codec_mute = CPCAP_AUDIO_CODEC_MUTE; @@ -212,6 +220,7 @@ static long cpcap_audio_ctl_ioctl(struct file *file, unsigned int cmd, rc = -EINVAL; goto done; } + switch (out.id) { case CPCAP_AUDIO_OUT_SPEAKER: pr_info("%s: setting output path to speaker\n", @@ -241,6 +250,15 @@ static long cpcap_audio_ctl_ioctl(struct file *file, unsigned int cmd, current_output = out; break; + case CPCAP_AUDIO_OUT_ANLG_DOCK_HEADSET: + pr_err("%s: setting output path to basic dock\n", + __func__); + if (out.on) + tegra_setup_audio_out_dock_headset_on(); + else + tegra_setup_audio_output_off(); + current_output = out; + break; case CPCAP_AUDIO_OUT_STANDBY: current_output.on = !out.on; if (out.on) { @@ -263,6 +281,11 @@ static long cpcap_audio_ctl_ioctl(struct file *file, unsigned int cmd, __func__); tegra_setup_audio_out_headset_and_speaker_on(); break; + case CPCAP_AUDIO_OUT_ANLG_DOCK_HEADSET: + pr_err("%s: standby off (dock headset)", + __func__); + tegra_setup_audio_out_dock_headset_on(); + break; } break; } diff --git a/include/linux/cpcap_audio.h b/include/linux/cpcap_audio.h index 58d008a81af0..7ec8aca4de96 100644 --- a/include/linux/cpcap_audio.h +++ b/include/linux/cpcap_audio.h @@ -27,7 +27,8 @@ #define CPCAP_AUDIO_OUT_HEADSET 1 #define CPCAP_AUDIO_OUT_HEADSET_AND_SPEAKER 2 #define CPCAP_AUDIO_OUT_STANDBY 3 -#define CPCAP_AUDIO_OUT_MAX 3 +#define CPCAP_AUDIO_OUT_ANLG_DOCK_HEADSET 4 +#define CPCAP_AUDIO_OUT_MAX 4 struct cpcap_audio_stream { unsigned id; /* e.g., CPCAP_AUDIO_OUT_SPEAKER or CPCAP_AUDIO_IN_MIC1 */ diff --git a/include/linux/spi/cpcap.h b/include/linux/spi/cpcap.h index 4774dbb3fbae..e7c5e3b18c5e 100644 --- a/include/linux/spi/cpcap.h +++ b/include/linux/spi/cpcap.h @@ -70,6 +70,7 @@ #define CPCAP_WHISPER_ACCY_MASK 0xF8000000 #define CPCAP_WHISPER_ACCY_SHFT 27 #define CPCAP_WHISPER_ID_SIZE 16 +#define CPCAP_WHISPER_PROP_SIZE 7 enum cpcap_regulator_id { CPCAP_SW2, @@ -627,6 +628,7 @@ struct cpcap_regacc { struct cpcap_whisper_request { unsigned int cmd; char dock_id[CPCAP_WHISPER_ID_SIZE]; + char dock_prop[CPCAP_WHISPER_PROP_SIZE]; }; /* @@ -779,8 +781,8 @@ int cpcap_uc_stop(struct cpcap_device *cpcap, enum cpcap_macro macro); unsigned char cpcap_uc_status(struct cpcap_device *cpcap, enum cpcap_macro macro); -int cpcap_accy_whisper(struct cpcap_device *cpcap, unsigned int cmd, - char *dock_id); +int cpcap_accy_whisper(struct cpcap_device *cpcap, + struct cpcap_whisper_request *req); void cpcap_accy_whisper_spdif_set_state(int state);