[ARM] mfd: cpcap-audio: add support for basic analog dock
authorPraveen Bharathi <pbharathi@motorola.com>
Thu, 14 Oct 2010 17:38:36 +0000 (12:38 -0500)
committerIliyan Malchev <malchev@google.com>
Wed, 3 Nov 2010 21:12:00 +0000 (14:12 -0700)
The audio will now be routed to dock accesory when an accesory connected to
dock is detected.

Signed-off-by: Praveen Bharathi <pbharathi@motorola.com>
Signed-off-by: Iliyan Malchev <malchev@google.com>
drivers/mfd/cpcap-audio-core.c
drivers/mfd/cpcap-core.c
drivers/mfd/cpcap-whisper.c
drivers/mfd/tegra-cpcap-audio.c
include/linux/cpcap_audio.h
include/linux/spi/cpcap.h

index c3027cdef21e4e84a7ef282e8042bc5de8c006e9..143c2592bbaa3d219dd530d0c9a374426529da75 100644 (file)
@@ -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, &reg_val);
        printk(KERN_INFO "0x20D[525] = %x\n", reg_val);
+       cpcap_regacc_read(state->cpcap, CPCAP_REG_USBC2, &reg_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);
 
index da12ce2b063ee457bc8857caf6183d4475966792..c56d688b050917ccec69732b42395a23f8eb5b7f 100644 (file)
@@ -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:
index 0d7dae5e6245faf06f7d9d22858d31d2e8d9f62c..a5a46e43743dd9f080d3e32e545f1dffd85c9c7e 100644 (file)
@@ -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);
index 77249dc083d8e1f65d1d37a2d4b729bc9c33a332..45e596a81fa99d4a0ee2d0faeaabd76ddbc8f4ac 100644 (file)
@@ -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;
                }
index 58d008a81af055ab2bc14dd76e703d8e11ac03a6..7ec8aca4de96ae7e69c4b678c8775ef663e64fb6 100644 (file)
@@ -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 */
index 4774dbb3fbae47b2f24b93b990e754caf0cffebc..e7c5e3b18c5efd4d566e6adc4ff5a80bd6eed02e 100644 (file)
@@ -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);