/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
#include <linux/etherdevice.h>
+#include <linux/usb/android_composite.h>
#include <asm/atomic.h>
/* .bInterfaceNumber = DYNAMIC */
/* status endpoint is optional; this could be patched later */
.bNumEndpoints = 1,
+#ifdef CONFIG_USB_ANDROID_RNDIS_WCEIS
+ /* "Wireless" RNDIS; auto-detected by Windows */
+ .bInterfaceClass = USB_CLASS_WIRELESS_CONTROLLER,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 3,
+#else
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
+#endif
/* .iInterface = DYNAMIC */
};
NULL,
};
+#ifdef CONFIG_USB_ANDROID_RNDIS
+static struct usb_ether_platform_data *rndis_pdata;
+#endif
+
/*-------------------------------------------------------------------------*/
static struct sk_buff *rndis_add_header(struct gether *port,
*/
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_SEND_ENCAPSULATED_COMMAND:
- if (w_length > req->length || w_value
- || w_index != rndis->ctrl_id)
+ if (w_value || w_index != rndis->ctrl_id)
goto invalid;
/* read the request; process it later */
value = w_length;
usb_ep_disable(rndis->notify);
} else {
VDBG(cdev, "init rndis ctrl %d\n", intf);
- rndis->notify_desc = ep_choose(cdev->gadget,
- rndis->hs.notify,
- rndis->fs.notify);
}
+ rndis->notify_desc = ep_choose(cdev->gadget,
+ rndis->hs.notify,
+ rndis->fs.notify);
usb_ep_enable(rndis->notify, rndis->notify_desc);
rndis->notify->driver_data = rndis;
if (!rndis->port.in) {
DBG(cdev, "init rndis\n");
- rndis->port.in = ep_choose(cdev->gadget,
- rndis->hs.in, rndis->fs.in);
- rndis->port.out = ep_choose(cdev->gadget,
- rndis->hs.out, rndis->fs.out);
}
+ rndis->port.in = ep_choose(cdev->gadget,
+ rndis->hs.in, rndis->fs.in);
+ rndis->port.out = ep_choose(cdev->gadget,
+ rndis->hs.out, rndis->fs.out);
/* Avoid ZLPs; they can be troublesome. */
rndis->port.is_zlp_ok = false;
rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0);
rndis_set_host_mac(rndis->config, rndis->ethaddr);
-#if 0
-// FIXME
- if (rndis_set_param_vendor(rndis->config, vendorID,
- manufacturer))
- goto fail0;
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ if (rndis_pdata) {
+ if (rndis_set_param_vendor(rndis->config, rndis_pdata->vendorID,
+ rndis_pdata->vendorDescr))
+ goto fail;
+ }
#endif
/* NOTE: all that is done without knowing or caring about
rndis->port.func.setup = rndis_setup;
rndis->port.func.disable = rndis_disable;
+#ifdef CONFIG_USB_ANDROID_RNDIS
+ /* start disabled */
+ rndis->port.func.disabled = 1;
+#endif
+
status = usb_add_function(c, &rndis->port.func);
if (status) {
kfree(rndis);
}
return status;
}
+
+#ifdef CONFIG_USB_ANDROID_RNDIS
+#include "rndis.c"
+
+static int __init rndis_probe(struct platform_device *pdev)
+{
+ rndis_pdata = pdev->dev.platform_data;
+ return 0;
+}
+
+static struct platform_driver rndis_platform_driver = {
+ .driver = { .name = "rndis", },
+ .probe = rndis_probe,
+};
+
+int rndis_function_bind_config(struct usb_configuration *c)
+{
+ int ret;
+
+ if (!rndis_pdata) {
+ printk(KERN_ERR "rndis_pdata null in rndis_function_bind_config\n");
+ return -1;
+ }
+
+ printk(KERN_INFO
+ "rndis_function_bind_config MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ rndis_pdata->ethaddr[0], rndis_pdata->ethaddr[1],
+ rndis_pdata->ethaddr[2], rndis_pdata->ethaddr[3],
+ rndis_pdata->ethaddr[4], rndis_pdata->ethaddr[5]);
+
+ ret = gether_setup(c->cdev->gadget, rndis_pdata->ethaddr);
+ if (ret == 0)
+ ret = rndis_bind_config(c, rndis_pdata->ethaddr);
+ return ret;
+}
+
+static struct android_usb_function rndis_function = {
+ .name = "rndis",
+ .bind_config = rndis_function_bind_config,
+};
+
+static int __init init(void)
+{
+ printk(KERN_INFO "f_rndis init\n");
+ platform_driver_register(&rndis_platform_driver);
+ android_register_function(&rndis_function);
+ return 0;
+}
+module_init(init);
+
+#endif /* CONFIG_USB_ANDROID_RNDIS */
#include <linux/kmemleak.h>
#include <asm/uaccess.h>
#include "internal.h"
+#include <linux/mtd/blktrans.h>
+#include <linux/mtd/mtd.h>
struct bdev_inode {
struct block_device bdev;
if (!bdev->bd_part)
goto out_clear;
+ ret = 0;
if (disk->fops->open) {
ret = disk->fops->open(bdev, mode);
if (ret == -ERESTARTSYS) {
mutex_unlock(&bdev->bd_mutex);
goto restart;
}
- if (ret)
- goto out_clear;
}
+ /*
+ * If the device is invalidated, rescan partition
+ * if open succeeded or failed with -ENOMEDIUM.
+ * The latter is necessary to prevent ghost
+ * partitions on a removed medium.
+ */
+ if (bdev->bd_invalidated && (!ret || ret == -ENOMEDIUM))
+ rescan_partitions(disk, bdev);
+ if (ret)
+ goto out_clear;
+
if (!bdev->bd_openers) {
bd_set_size(bdev,(loff_t)get_capacity(disk)<<9);
bdi = blk_get_backing_dev_info(bdev);
bdi = &default_backing_dev_info;
bdev->bd_inode->i_data.backing_dev_info = bdi;
}
- if (bdev->bd_invalidated)
- rescan_partitions(disk, bdev);
} else {
struct block_device *whole;
whole = bdget_disk(disk, 0);
put_disk(disk);
disk = NULL;
if (bdev->bd_contains == bdev) {
- if (bdev->bd_disk->fops->open) {
+ ret = 0;
+ if (bdev->bd_disk->fops->open)
ret = bdev->bd_disk->fops->open(bdev, mode);
- if (ret)
- goto out_unlock_bdev;
- }
- if (bdev->bd_invalidated)
+ /* the same as first opener case, read comment there */
+ if (bdev->bd_invalidated && (!ret || ret == -ENOMEDIUM))
rescan_partitions(bdev->bd_disk, bdev);
+ if (ret)
+ goto out_unlock_bdev;
}
}
bdev->bd_openers++;
.direct_IO = blkdev_direct_IO,
};
+
+ssize_t mydo_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
+{
+ unsigned long buf_addr = (unsigned long)buf;
+ if(memcmp(filp->f_mapping->host->i_bdev->bd_disk->disk_name, "mtdblock", 8) == 0 )// kernel mem is usb tran &&(buf_addr >= 0xc0000000)
+ {
+ struct mtd_blktrans_dev *dev;
+ struct mtd_blktrans_ops *tr;
+ struct mtd_info *mtd;
+
+ dev = (filp->f_mapping->host->i_bdev->bd_disk->private_data);
+ mtd = dev->mtd;
+ if((buf_addr < 0xc0000000)&&(mtd->name[0]=='u' &&mtd->name[3]=='r' && mtd->name[4]==0)) // user part
+ {
+ return(do_sync_read(filp, buf,len,ppos));
+ }
+ tr = dev->tr;
+ if (!tr->readsect)
+ {
+ return(do_sync_read(filp, buf,len,ppos));
+ }
+ //printk("mydo_sync_read buf = 0x%lx LBA = 0x%lx len = 0x%x \n",buf, (unsigned long)(*ppos>>9),len);
+ if(tr->readsect(dev, (unsigned long)(*ppos>>9), len>>9, buf))
+ {
+ return 0 ;
+ }
+ *ppos += len;
+ return len;
+ }
+
+ else
+ {
+ return(do_sync_read(filp, buf,len,ppos));
+ }
+}
+
+ssize_t mydo_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
+{
+ unsigned long buf_addr = (unsigned long)buf;
+ if(memcmp(filp->f_mapping->host->i_bdev->bd_disk->disk_name, "mtdblock", 8) == 0)// kernel mem is usb tran &&(buf_addr >= 0xc0000000)
+ {
+ struct mtd_blktrans_dev *dev;
+ struct mtd_blktrans_ops *tr;
+ struct mtd_info *mtd;
+
+ dev = (filp->f_mapping->host->i_bdev->bd_disk->private_data);
+
+ mtd = dev->mtd;
+ if((buf_addr < 0xc0000000)&&(mtd->name[0]=='u' &&mtd->name[3]=='r' && mtd->name[4]==0))
+ {
+ return(do_sync_write(filp, buf,len,ppos));
+ }
+
+ tr = dev->tr;
+
+ if (!tr->writesect)
+ return 0;
+ //printk("mydo_sync_write buf = 0x%lx LBA = 0x%lx len = 0x%x \n",buf, (unsigned long)(*ppos>>9),len);
+ if(tr->writesect(dev, (unsigned long)(*ppos>>9), len>>9, buf))
+ {
+ return 0 ;
+ }
+ *ppos += len;
+ return len;
+ }
+
+ else
+ {
+ return(do_sync_write(filp, buf,len,ppos));
+ }
+}
+
+
const struct file_operations def_blk_fops = {
.open = blkdev_open,
.release = blkdev_close,
.llseek = block_llseek,
- .read = do_sync_read,
- .write = do_sync_write,
+ .read = mydo_sync_read,
+ .write = mydo_sync_write,
.aio_read = generic_file_aio_read,
.aio_write = blkdev_aio_write,
.mmap = generic_file_mmap,
static const struct soc_enum speaker_mode =
SOC_ENUM_SINGLE(WM8993_SPKMIXR_ATTENUATION, 8, 2, speaker_mode_text);
-static void wait_for_dc_servo(struct snd_soc_codec *codec)
+static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op)
{
unsigned int reg;
int count = 0;
+ unsigned int val;
+
+ val = op | WM8993_DCS_ENA_CHAN_0 | WM8993_DCS_ENA_CHAN_1;
+
+ /* Trigger the command */
+ snd_soc_write(codec, WM8993_DC_SERVO_0, val);
+
+ dev_vdbg(codec->dev, "Waiting for DC servo...\n");
- dev_dbg(codec->dev, "Waiting for DC servo...\n");
do {
count++;
- msleep(1);
- reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_0);
- dev_dbg(codec->dev, "DC servo status: %x\n", reg);
- } while ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
- != WM8993_DCS_CAL_COMPLETE_MASK && count < 1000);
-
- if ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
- != WM8993_DCS_CAL_COMPLETE_MASK)
+ msleep(10);
+ reg = snd_soc_read(codec, WM8993_DC_SERVO_0);
+ dev_vdbg(codec->dev, "DC servo: %x\n", reg);
+ } while (reg & op && count < 400);
+
+ if (reg & op)
dev_err(codec->dev, "Timed out waiting for DC Servo\n");
}
+/*
+ * Startup calibration of the DC servo
+ */
+static void calibrate_dc_servo(struct snd_soc_codec *codec)
+{
+ struct wm_hubs_data *hubs = codec->private_data;
+ u16 reg, reg_l, reg_r, dcs_cfg;
+
+ /* Set for 32 series updates */
+ snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
+ WM8993_DCS_SERIES_NO_01_MASK,
+ 32 << WM8993_DCS_SERIES_NO_01_SHIFT);
+ wait_for_dc_servo(codec,
+ WM8993_DCS_TRIG_SERIES_0 | WM8993_DCS_TRIG_SERIES_1);
+
+ /* Apply correction to DC servo result */
+ if (hubs->dcs_codes) {
+ dev_dbg(codec->dev, "Applying %d code DC servo correction\n",
+ hubs->dcs_codes);
+
+ /* Different chips in the family support different
+ * readback methods.
+ */
+ switch (hubs->dcs_readback_mode) {
+ case 0:
+ reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1)
+ & WM8993_DCS_INTEG_CHAN_0_MASK;;
+ reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
+ & WM8993_DCS_INTEG_CHAN_1_MASK;
+ break;
+ case 1:
+ reg = snd_soc_read(codec, WM8993_DC_SERVO_3);
+ reg_l = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
+ >> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
+ reg_r = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
+ break;
+ default:
+ WARN(1, "Unknown DCS readback method");
+ break;
+ }
+
+ /* HPOUT1L */
+ if (reg_l + hubs->dcs_codes > 0 &&
+ reg_l + hubs->dcs_codes < 0xff)
+ reg_l += hubs->dcs_codes;
+ dcs_cfg = reg_l << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
+
+ /* HPOUT1R */
+ if (reg_r + hubs->dcs_codes > 0 &&
+ reg_r + hubs->dcs_codes < 0xff)
+ reg_r += hubs->dcs_codes;
+ dcs_cfg |= reg_r;
+
+ /* Do it */
+ snd_soc_write(codec, WM8993_DC_SERVO_3, dcs_cfg);
+ wait_for_dc_servo(codec,
+ WM8993_DCS_TRIG_DAC_WR_0 |
+ WM8993_DCS_TRIG_DAC_WR_1);
+ }
+}
+
/*
* Update the DC servo calibration on gain changes
*/
static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm_hubs_data *hubs = codec->private_data;
int ret;
ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
+ /* If we're applying an offset correction then updating the
+ * callibration would be likely to introduce further offsets. */
+ if (hubs->dcs_codes)
+ return ret;
+
/* Only need to do this if the outputs are active */
if (snd_soc_read(codec, WM8993_POWER_MANAGEMENT_1)
& (WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA))
line_tlv),
};
+static int hp_supply_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct wm_hubs_data *hubs = codec->private_data;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (hubs->hp_startup_mode) {
+ case 0:
+ break;
+ case 1:
+ /* Enable the headphone amp */
+ snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+ WM8993_HPOUT1L_ENA |
+ WM8993_HPOUT1R_ENA,
+ WM8993_HPOUT1L_ENA |
+ WM8993_HPOUT1R_ENA);
+
+ /* Enable the second stage */
+ snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+ WM8993_HPOUT1L_DLY |
+ WM8993_HPOUT1R_DLY,
+ WM8993_HPOUT1L_DLY |
+ WM8993_HPOUT1R_DLY);
+ break;
+ default:
+ dev_err(codec->dev, "Unknown HP startup mode %d\n",
+ hubs->hp_startup_mode);
+ break;
+ }
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
+ WM8993_CP_ENA, 0);
+ break;
+ }
+
+ return 0;
+}
+
static int hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA);
-
+ // printk("hp_event power1 up: 0x%04x",snd_soc_read(codec,WM8993_POWER_MANAGEMENT_1));
reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY;
snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
- /* Start the DC servo */
- snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
- 0xFFFF,
- WM8993_DCS_ENA_CHAN_0 |
- WM8993_DCS_ENA_CHAN_1 |
- WM8993_DCS_TRIG_STARTUP_1 |
- WM8993_DCS_TRIG_STARTUP_0);
- wait_for_dc_servo(codec);
+ /* Smallest supported update interval */
+ snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
+ WM8993_DCS_TIMER_PERIOD_01_MASK, 1);
+
+ calibrate_dc_servo(codec);
reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
break;
case SND_SOC_DAPM_PRE_PMD:
- reg &= ~(WM8993_HPOUT1L_RMV_SHORT |
- WM8993_HPOUT1L_DLY |
- WM8993_HPOUT1L_OUTP |
- WM8993_HPOUT1R_RMV_SHORT |
- WM8993_HPOUT1R_DLY |
- WM8993_HPOUT1R_OUTP);
+ snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+ WM8993_HPOUT1L_DLY |
+ WM8993_HPOUT1R_DLY |
+ WM8993_HPOUT1L_RMV_SHORT |
+ WM8993_HPOUT1R_RMV_SHORT, 0);
- snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
- 0xffff, 0);
+ snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+ WM8993_HPOUT1L_OUTP |
+ WM8993_HPOUT1R_OUTP, 0);
- snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
0);
-
- snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
- WM8993_CP_ENA, 0);
+ // printk("hp_event power1 down: 0x%04x",snd_soc_read(codec,WM8993_POWER_MANAGEMENT_1));
break;
}
SND_SOC_DAPM_INPUT("IN1LN"),
SND_SOC_DAPM_INPUT("IN1LP"),
SND_SOC_DAPM_INPUT("IN2LN"),
-SND_SOC_DAPM_INPUT("IN2LP/VXRN"),
+SND_SOC_DAPM_INPUT("IN2LP:VXRN"),
SND_SOC_DAPM_INPUT("IN1RN"),
SND_SOC_DAPM_INPUT("IN1RP"),
SND_SOC_DAPM_INPUT("IN2RN"),
-SND_SOC_DAPM_INPUT("IN2RP/VXRP"),
+SND_SOC_DAPM_INPUT("IN2RP:VXRP"),
SND_SOC_DAPM_MICBIAS("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0),
SND_SOC_DAPM_MICBIAS("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0),
SND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0),
SND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("Headphone Supply", SND_SOC_NOPM, 0, 0, hp_supply_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_E("Headphone PGA", SND_SOC_NOPM, 0, 0,
NULL, 0,
hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
};
static const struct snd_soc_dapm_route analogue_routes[] = {
+ { "IN1L PGA", NULL , "MICBIAS2" },
+ { "IN1R PGA", NULL , "MICBIAS1" },
+ { "MICBIAS2", NULL , "IN1LP"},
+ { "MICBIAS2", NULL , "IN1LN"},
+ { "MICBIAS1", NULL , "IN1RP"},
+ { "MICBIAS1", NULL , "IN1RN"},
+
{ "IN1L PGA", "IN1LP Switch", "IN1LP" },
{ "IN1L PGA", "IN1LN Switch", "IN1LN" },
{ "IN1R PGA", "IN1RP Switch", "IN1RP" },
{ "IN1R PGA", "IN1RN Switch", "IN1RN" },
- { "IN2L PGA", "IN2LP Switch", "IN2LP/VXRN" },
+ { "IN2L PGA", "IN2LP Switch", "IN2LP:VXRN" },
{ "IN2L PGA", "IN2LN Switch", "IN2LN" },
- { "IN2R PGA", "IN2RP Switch", "IN2RP/VXRP" },
+ { "IN2R PGA", "IN2RP Switch", "IN2RP:VXRP" },
{ "IN2R PGA", "IN2RN Switch", "IN2RN" },
- { "Direct Voice", NULL, "IN2LP/VXRN" },
- { "Direct Voice", NULL, "IN2RP/VXRP" },
+ { "Direct Voice", NULL, "IN2LP:VXRN" },
+ { "Direct Voice", NULL, "IN2RP:VXRP" },
{ "MIXINL", "IN1L Switch", "IN1L PGA" },
{ "MIXINL", "IN2L Switch", "IN2L PGA" },
{ "Left Output Mixer", "Right Input Switch", "MIXINR" },
{ "Left Output Mixer", "IN2RN Switch", "IN2RN" },
{ "Left Output Mixer", "IN2LN Switch", "IN2LN" },
- { "Left Output Mixer", "IN2LP Switch", "IN2LP/VXRN" },
+ { "Left Output Mixer", "IN2LP Switch", "IN2LP:VXRN" },
{ "Left Output Mixer", "IN1L Switch", "IN1L PGA" },
{ "Left Output Mixer", "IN1R Switch", "IN1R PGA" },
{ "Right Output Mixer", "Right Input Switch", "MIXINR" },
{ "Right Output Mixer", "IN2LN Switch", "IN2LN" },
{ "Right Output Mixer", "IN2RN Switch", "IN2RN" },
- { "Right Output Mixer", "IN2RP Switch", "IN2RP/VXRP" },
+ { "Right Output Mixer", "IN2RP Switch", "IN2RP:VXRP" },
{ "Right Output Mixer", "IN1L Switch", "IN1L PGA" },
{ "Right Output Mixer", "IN1R Switch", "IN1R PGA" },
{ "Headphone PGA", NULL, "Left Headphone Mux" },
{ "Headphone PGA", NULL, "Right Headphone Mux" },
{ "Headphone PGA", NULL, "CLK_SYS" },
+ { "Headphone PGA", NULL, "Headphone Supply" },
{ "HPOUT1L", NULL, "Headphone PGA" },
{ "HPOUT1R", NULL, "Headphone PGA" },
static const struct snd_soc_dapm_route lineout1_diff_routes[] = {
{ "LINEOUT1 Mixer", "IN1L Switch", "IN1L PGA" },
{ "LINEOUT1 Mixer", "IN1R Switch", "IN1R PGA" },
- { "LINEOUT1 Mixer", "Output Switch", "Left Output Mixer" },
+ { "LINEOUT1 Mixer", "Output Switch", "Left Output PGA" },
{ "LINEOUT1N Driver", NULL, "LINEOUT1 Mixer" },
{ "LINEOUT1P Driver", NULL, "LINEOUT1 Mixer" },
};
static const struct snd_soc_dapm_route lineout1_se_routes[] = {
- { "LINEOUT1N Mixer", "Left Output Switch", "Left Output Mixer" },
- { "LINEOUT1N Mixer", "Right Output Switch", "Left Output Mixer" },
+ { "LINEOUT1N Mixer", "Left Output Switch", "Left Output PGA" },
+ { "LINEOUT1N Mixer", "Right Output Switch", "Right Output PGA" },
- { "LINEOUT1P Mixer", "Left Output Switch", "Left Output Mixer" },
+ { "LINEOUT1P Mixer", "Left Output Switch", "Left Output PGA" },
{ "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" },
{ "LINEOUT1P Driver", NULL, "LINEOUT1P Mixer" },
static const struct snd_soc_dapm_route lineout2_diff_routes[] = {
{ "LINEOUT2 Mixer", "IN2L Switch", "IN2L PGA" },
{ "LINEOUT2 Mixer", "IN2R Switch", "IN2R PGA" },
- { "LINEOUT2 Mixer", "Output Switch", "Right Output Mixer" },
+ { "LINEOUT2 Mixer", "Output Switch", "Right Output PGA" },
{ "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" },
{ "LINEOUT2P Driver", NULL, "LINEOUT2 Mixer" },
};
static const struct snd_soc_dapm_route lineout2_se_routes[] = {
- { "LINEOUT2N Mixer", "Left Output Switch", "Left Output Mixer" },
- { "LINEOUT2N Mixer", "Right Output Switch", "Left Output Mixer" },
+ { "LINEOUT2N Mixer", "Left Output Switch", "Left Output PGA" },
+ { "LINEOUT2N Mixer", "Right Output Switch", "Right Output PGA" },
- { "LINEOUT2P Mixer", "Right Output Switch", "Right Output Mixer" },
+ { "LINEOUT2P Mixer", "Right Output Switch", "Right Output PGA" },
{ "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" },
{ "LINEOUT2P Driver", NULL, "LINEOUT2P Mixer" },
snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_3_4_VOLUME,
WM8993_IN2_VU, WM8993_IN2_VU);
+ snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_LEFT,
+ WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_RIGHT,
WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
snd_soc_update_bits(codec, WM8993_LEFT_OUTPUT_VOLUME,
- WM8993_HPOUT1L_ZC, WM8993_HPOUT1L_ZC);
+ WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC,
+ WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC);
snd_soc_update_bits(codec, WM8993_RIGHT_OUTPUT_VOLUME,
WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC,
WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC);
snd_soc_update_bits(codec, WM8993_LEFT_OPGA_VOLUME,
- WM8993_MIXOUTL_ZC, WM8993_MIXOUTL_ZC);
+ WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU,
+ WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU);
snd_soc_update_bits(codec, WM8993_RIGHT_OPGA_VOLUME,
WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU,
WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU);
}
EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_routes);
+int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec,
+ int lineout1_diff, int lineout2_diff,
+ int lineout1fb, int lineout2fb,
+ int jd_scthr, int jd_thr, int micbias1_lvl,
+ int micbias2_lvl)
+{
+ if (!lineout1_diff)
+ snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
+ WM8993_LINEOUT1_MODE,
+ WM8993_LINEOUT1_MODE);
+ if (!lineout2_diff)
+ snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
+ WM8993_LINEOUT2_MODE,
+ WM8993_LINEOUT2_MODE);
+
+ /* If the line outputs are differential then we aren't presenting
+ * VMID as an output and can disable it.
+ */
+// if (lineout1_diff && lineout2_diff)
+// codec->idle_bias_off = 1;
+
+ if (lineout1fb)
+ snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+ WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
+
+ if (lineout2fb)
+ snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+ WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
+
+ snd_soc_update_bits(codec, WM8993_MICBIAS,
+ WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
+ WM8993_MICB1_LVL | WM8993_MICB2_LVL,
+ jd_scthr << WM8993_JD_SCTHR_SHIFT |
+ jd_thr << WM8993_JD_THR_SHIFT |
+ micbias1_lvl |
+ micbias2_lvl << WM8993_MICB2_LVL_SHIFT);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_hubs_handle_analogue_pdata);
+
MODULE_DESCRIPTION("Shared support for Wolfson hubs products");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");