diff options
| -rw-r--r-- | arch/arm/boot/dts/omap3_h1.dts | 29 | ||||
| -rw-r--r-- | arch/arm/mach-omap2/board-omap3h1.c | 9 | ||||
| -rw-r--r-- | drivers/i2c/busses/i2c-omap.c | 2 | ||||
| -rw-r--r-- | drivers/power/bq27x00_battery.c | 218 | ||||
| -rw-r--r-- | drivers/staging/triune/ts81001.c | 272 | ||||
| -rw-r--r-- | drivers/staging/triune/ts81001.h | 4 | ||||
| -rw-r--r-- | sound/soc/codecs/Kconfig | 4 | ||||
| -rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
| -rw-r--r-- | sound/soc/codecs/omap3h1_bt_sco.c | 74 | ||||
| -rw-r--r-- | sound/soc/omap/Kconfig | 1 | ||||
| -rw-r--r-- | sound/soc/omap/omap3h1.c | 197 |
11 files changed, 665 insertions, 147 deletions
diff --git a/arch/arm/boot/dts/omap3_h1.dts b/arch/arm/boot/dts/omap3_h1.dts index 7b922bb66b0..46c6e2d97b1 100644 --- a/arch/arm/boot/dts/omap3_h1.dts +++ b/arch/arm/boot/dts/omap3_h1.dts @@ -68,7 +68,7 @@ * for a gpio IRQ (banks numbered 1 to 6) * irq = 16 + 96 + (gpio_bank - 1) * 32 + GPIO no. */ - compatible = "ti,pad-wkup"; + compatible = "ti,pad-wkup"; /* Map the pad offset (off) to an interrupt (IRQ). */ /* if handle is 1, an irq will be generated based on */ @@ -83,15 +83,16 @@ /* off IRQ handle */ ti,pad_irq = <0x150 88 1>, /* 72 + 16, uart1 - BT input */ - <0x9f6 143 1>, /* mpu6515 irq pin - is this offset correct? */ - <0x9ea 122 1>, /* bq27400 chg irq8 */ - <0x9f4 123 1>, /* BT host wake */ - <0x1b0 23 1>; /* sys_nirq */ + <0x9f6 143 1>, /* mpu6515 irq pin - is this offset correct? */ + /* <0x9ea 122 1>, */ /* bq27400 chg irq8 - needed here? */ + <0x9f4 123 1>, /* BT host wake */ + <0x1b0 23 1>; /* sys_nirq */ }; sound { compatible = "olio,omap-soc-omap3h1"; - olio,mcbsp = <&mcbsp3>; + olio,mcbsp_bt = <&mcbsp2>; + olio,mcbsp_mic = <&mcbsp3>; olio,mic_enable = <&gpio5 18 0>; }; }; @@ -215,14 +216,14 @@ >; }; - dev_pins: pinmux_pv_pins { - pinctrl-single,pins = < - 0x5b2 0x204 /* USB control, ETK_D3, MODE4 | OUTPUT */ - /* 0x1a2 0x104 */ /* ALS interrupt, input, GPIO */ - 0x086 0x004 /* DRV2605 vibrator, output, GPIO */ - /* 0x938 0x104 */ /* Battery state, input, GPIO */ - >; - }; +dev_pins: pinmux_pv_pins { + pinctrl-single,pins = < + 0x5b2 0x204 /* USB control, ETK_D3, MODE4 | OUTPUT */ + /* 0x1a2 0x104 */ /* ALS interrupt, input, GPIO */ + 0x086 0x004 /* DRV2605 vibrator, output, GPIO */ + /* 0x938 0x104 */ /* Battery state, input, GPIO */ + >; + }; /* i2c1_pins: pinmux_i2c1_pins { diff --git a/arch/arm/mach-omap2/board-omap3h1.c b/arch/arm/mach-omap2/board-omap3h1.c index ad40bd6d90e..7915962e45b 100644 --- a/arch/arm/mach-omap2/board-omap3h1.c +++ b/arch/arm/mach-omap2/board-omap3h1.c @@ -254,6 +254,10 @@ static struct platform_device omap3h1_dmic_codec = { .id = -1, }; +static struct platform_device omap3h1_bt_codec = { + .name = "omap3h1_bt_sco", + .id = -1, +}; /* --------------------------------------------------------------------------- */ /* USB settings @@ -473,9 +477,10 @@ static int __init omap3_h1_i2c_init(void) static struct platform_device *omap3h1_devices[] __initdata = { - &bcm20702_bluetooth_device, - &nop_phy_device, + &bcm20702_bluetooth_device, + &nop_phy_device, &omap3h1_dmic_codec, + &omap3h1_bt_codec, }; static void __init omap3_h1_init(void) diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 0e15ad357aa..00fb5ac811c 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -690,7 +690,7 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) r = omap_i2c_wait_for_bb(dev); /* If timeout, try to again check after attempting to clear the bus */ - if (WARN_ON(r == -ETIMEDOUT)) { + if (r == -ETIMEDOUT) { /* removed WARN_ON here, too much output from ts81001 */ r = omap_i2c_bus_clear(dev); if (r < 0) { dev_err(dev->dev, "Unable to recover i2c bus from bb\n"); diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index c26859a2f84..8002531960e 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -47,18 +47,30 @@ #include <linux/power/bq27x00_battery.h> #ifdef CONFIG_OLIO_TS81001 + +/* + * OLIO Model One needs the fuel gauge to talk to the charger in some + * instances, such as deciding whether we're using the wireless + * charger or not. Hence this build flag. + * + * In hindsight, a lot of this can probably be moved to the charger + * manager. + */ +#include <linux/alarmtimer.h> +#include "asm/io.h" +#include "../../arch/arm/mach-omap2/mux.h" +#include "../../arch/arm/mach-omap2/iomap.h" #include "../staging/triune/ts81001.h" #endif /* CONFIG_OLIO_TS81001 */ - #ifdef OLIODEBUG -#define do { olio_debug(format, ...) printk("OLIO %s: ", __FUNCTION__); \ +#define olio_debug(format, ...) do { \ + printk("OLIO %s: ", __FUNCTION__); \ printk(format, ##__VA_ARGS__); } while(0) #else #define olio_debug(format, ...) #endif - #define DRIVER_VERSION "1.2.0" #define INVALID_REG_ADDR 0xFF @@ -84,8 +96,14 @@ enum bq27xxx_reg_index { NUM_REGS }; +/* lock taken if we want the upper layers to run */ + struct wake_lock chg_wake_lock; +/* lock taken by interrupt to let work queue run*/ + +struct wake_lock isr_wake_lock; + /* bq27500 registers */ static __initdata u8 bq27500_regs[NUM_REGS] = { 0x00, /* CONTROL */ @@ -226,7 +244,7 @@ static __initdata u8 bq276xx_regs[NUM_REGS] = { #define BQ274XX_UNSEAL_KEY 0x80008000 #define BQ274XX_SOFT_RESET 0x43 -#define BQ274XX_FLAG_ITPOR 0x20 +#define BQ274XX_FLAG_ITPOR 0x20 #define BQ274XX_CTRL_STATUS_INITCOMP 0x80 #define BQ27XXX_FLAG_DSC BIT(0) @@ -261,6 +279,8 @@ static __initdata u8 bq276xx_regs[NUM_REGS] = { #define BQ276XX_OP_CFG_B_OFFSET 2 #define BQ276XX_OP_CFG_B_DEF_SEAL_BIT (1 << 5) +#define BQ27X00_ALARM_TIMER_INTERVAL 300 /* 5 minutes */ + struct bq27x00_device_info; struct bq27x00_access_methods { int (*read)(struct bq27x00_device_info *di, u8 reg, bool single); @@ -306,6 +326,7 @@ struct bq27x00_device_info { unsigned long last_update; struct delayed_work work; + struct delayed_work interrupt_work; struct power_supply bat; @@ -318,6 +339,9 @@ struct bq27x00_device_info { u8 regs[NUM_REGS]; struct dm_reg *dm_regs; u16 dm_regs_count; + + struct alarm poll_alarm; + ktime_t alarm_time; }; static __initdata enum power_supply_property bq27x00_battery_props[] = { @@ -410,6 +434,9 @@ static __initdata enum power_supply_property bq276xx_battery_props[] = { * Ordering the parameters based on subclass and then offset will help in * having fewer flash writes while updating. * Customize these values and, if necessary, add more based on system needs. + + To be added, maybe (for bq274xx): + {81, 9, 1, 3}, Quit Relax Time (3s) */ static struct dm_reg bq274xx_dm_regs[] = { {36, 6, 1, 90}, /* FC Clear % */ @@ -438,6 +465,8 @@ module_param(poll_interval, uint, 0644); MODULE_PARM_DESC(poll_interval, "battery poll interval in seconds - " \ "0 disables polling"); +static volatile int mA_now; + /* * Forward Declarations */ @@ -445,6 +474,14 @@ static int read_dm_block(struct bq27x00_device_info *di, u8 subclass, u8 offset, u8 *data); +#ifdef CONFIG_OLIO_TS81001 +static int bq27x00_charger_status(struct bq27x00_device_info *di, + union power_supply_propval *val); +static int bq27x00_battery_status(struct bq27x00_device_info *di, + union power_supply_propval *val); +#endif + + /* * Common code for BQ27x00 devices */ @@ -974,7 +1011,12 @@ static void bq27x00_update(struct bq27x00_device_info *di) bool is_bq274xx = di->chip == BQ274XX; bool is_bq276xx = di->chip == BQ276XX; + olio_debug ("entered\n"); + cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, !is_bq27500); + + olio_debug ("Cache flags read from bq is 0x%x\n", cache.flags); + if (cache.flags >= 0) { if (is_bq27200 && (cache.flags & BQ27200_FLAG_CI)) { dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n"); @@ -1060,10 +1102,11 @@ static bool rom_mode_gauge_dm_initialized(struct bq27x00_device_info *di) dev_dbg(di->dev, "%s: flags - 0x%04x\n", __func__, flags); - if (flags & BQ274XX_FLAG_ITPOR) + if (flags & BQ274XX_FLAG_ITPOR) { return false; - else + } else { return true; + } } #define INITCOMP_TIMEOUT_MS 10000 @@ -1103,6 +1146,9 @@ static void rom_mode_gauge_dm_init(struct bq27x00_device_info *di) subclass = dm_reg->subclass; offset = dm_reg->offset; + olio_debug ("updating reg %d, offset %d\n", + subclass, offset); + /* * Create a composite block number to see if the subsequent * register also belongs to the same 32 btye block in the DM @@ -1136,10 +1182,40 @@ static void rom_mode_gauge_dm_init(struct bq27x00_device_info *di) exit_cfg_update_mode(di); } -static void bq27x00_battery_poll(struct work_struct *work) + +/*************************************************************************** + * bq27x00_battery_interrupt - work to do from interrupt + * + * Decide whether we actually want to wake up, then take a wake lock if + * appropriate. + */ + +static void bq27x00_battery_interrupt (struct work_struct *i_work) { + int status; + union power_supply_propval val; struct bq27x00_device_info *di = - container_of(work, struct bq27x00_device_info, work.work); + container_of(i_work, struct bq27x00_device_info, interrupt_work.work); + + olio_debug ("taking charge wake lock\n"); + + wake_lock_timeout(&chg_wake_lock, msecs_to_jiffies(500)); + bq27x00_update (di); + status = bq27x00_battery_status(di, &val); + + olio_debug ("battery status = %d\n", status); + + /* release the low-level wake lock (taken in isr handler) */ + wake_unlock (&isr_wake_lock); +} + + +static void bq27x00_battery_poll(struct work_struct *p_work) +{ + struct bq27x00_device_info *di = + container_of(p_work, struct bq27x00_device_info, work.work); + + olio_debug ("entered\n"); if (((di->chip == BQ274XX) || (di->chip == BQ276XX)) && !rom_mode_gauge_dm_initialized(di)) { @@ -1155,13 +1231,33 @@ static void bq27x00_battery_poll(struct work_struct *work) } } +static enum alarmtimer_restart +bq27x00_run_poll_alarm(struct alarm * p_alarm, ktime_t now) +{ + struct bq27x00_device_info *di = + container_of(p_alarm, struct bq27x00_device_info, poll_alarm); + + olio_debug ("entered\n"); + + wake_lock_timeout(&chg_wake_lock, msecs_to_jiffies(500)); + + cancel_delayed_work(&di->work); + schedule_delayed_work(&di->work, 5); + + /* reschedule manually, since restart doesn't seem to do what we want. */ + + alarm_start_relative (&di->poll_alarm, di->alarm_time); + + return ALARMTIMER_NORESTART; +} + /* * Return the battery average current in µA * Note that current can be negative signed as well * Or 0 if something fails. */ static int bq27x00_battery_current(struct bq27x00_device_info *di, - union power_supply_propval *val) + union power_supply_propval *val) { int curr; int flags; @@ -1190,7 +1286,36 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di, #ifdef CONFIG_OLIO_TS81001 -static int bq27x00_charger_status(struct bq27x00_device_info *di) { +/* These are unfortunately hardcoded to our (OLIO's) GPIO pins right now. */ + +/* We need to turn off the wakeup capability of the pins when we don't + * want the interrupts, or all heck breaks loose. + */ + +static void olio_interrupt_enabled (int irq) { + __raw_writew ((u16) (4 << 12 | 1 << 8 | 0 << 4 | 4), + (volatile void *) + OMAP2_L4_IO_ADDRESS(OMAP3_CONTROL_PADCONF_MUX_PBASE) + + OMAP3_CONTROL_PADCONF_SYS_CLKOUT1_OFFSET); + enable_irq (irq); + enable_irq_wake (irq); +} + + +static void olio_interrupt_disabled (int irq) { + disable_irq_wake (irq); + disable_irq (irq); + __raw_writew ((u16) (0 << 12 | 1 << 8 | 0 << 4 | 4), + (volatile void *) + OMAP2_L4_IO_ADDRESS(OMAP3_CONTROL_PADCONF_MUX_PBASE) + + OMAP3_CONTROL_PADCONF_SYS_CLKOUT1_OFFSET); +} + + +static volatile int irq_enabled = 1; /* enabled from start */ + +static int bq27x00_charger_status (struct bq27x00_device_info *di, + union power_supply_propval *val) { struct device * bq = di->dev; struct bq27x00_platform_data * pdata = bq->platform_data; struct ts81001 * ts81001; @@ -1207,9 +1332,21 @@ static int bq27x00_charger_status(struct bq27x00_device_info *di) { olio_debug("Got client data for dev %s\n", dev_name(ts81001->dev)); - if(di->cache.flags & BQ27XXX_FLAG_CHG) { - ts81001->ops->request_charge(ts81001); +#if 0 + /* Test 1 */ + if(di->cache.flags & BQ27XXX_FLAG_FC) { + ts81001->ops->request_charge(ts81001, 100); + } else { + ts81001->ops->request_charge(ts81001, 390); + } + + /* Test 2 */ + olio_debug ("mA_now == %d \n", mA_now); + + if (mA_now < 0) { + ts81001->ops->request_charge(ts81001, 100); } +#endif state = ts81001->ops->get_status(ts81001); @@ -1219,11 +1356,27 @@ static int bq27x00_charger_status(struct bq27x00_device_info *di) { { case NOT_CHARGING: olio_debug("exiting, DISCHARGING\n"); + mutex_lock(&di->lock); + if (!irq_enabled) { /* set up for off charger behavior */ + olio_debug ("off charger, enabling chg interrupts\n"); + irq_enabled = 1; + olio_interrupt_enabled (di->irq); + alarm_cancel (&di->poll_alarm); + } + mutex_unlock(&di->lock); return POWER_SUPPLY_STATUS_DISCHARGING; case PRECHARGE: case CHARGING_1C: case TOPOFF: olio_debug("exiting, CHARGING\n"); + mutex_lock(&di->lock); + if (irq_enabled) { /* set up for on charger behavior */ + olio_debug ("on charger, disabling chg interrupts\n"); + irq_enabled = 0; + olio_interrupt_disabled (di->irq); + alarm_start_relative (&di->poll_alarm, di->alarm_time); + } + mutex_unlock(&di->lock); return POWER_SUPPLY_STATUS_CHARGING; default: olio_debug("Couldn't read status from ts81001 charger.\n"); @@ -1231,7 +1384,6 @@ static int bq27x00_charger_status(struct bq27x00_device_info *di) { return POWER_SUPPLY_STATUS_UNKNOWN; } } - #endif static int bq27x00_battery_status(struct bq27x00_device_info *di, @@ -1250,7 +1402,7 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di, status = POWER_SUPPLY_STATUS_DISCHARGING; } else { #ifdef CONFIG_OLIO_TS81001 - status = bq27x00_charger_status (di); + status = bq27x00_charger_status (di, val); if(status == POWER_SUPPLY_STATUS_CHARGING && di->cache.flags & BQ27XXX_FLAG_FC) { @@ -1364,6 +1516,7 @@ static int bq27x00_battery_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CURRENT_NOW: ret = bq27x00_battery_current(di, val); + mA_now = val->intval; break; case POWER_SUPPLY_PROP_CAPACITY: ret = bq27x00_simple_value(di->cache.capacity, val); @@ -1416,6 +1569,7 @@ static int bq27x00_battery_get_property(struct power_supply *psy, return ret; } + static void bq27x00_external_power_changed(struct power_supply *psy) { struct bq27x00_device_info *di = to_bq27x00_device_info(psy); @@ -1424,14 +1578,17 @@ static void bq27x00_external_power_changed(struct power_supply *psy) schedule_delayed_work(&di->work, 0); } -static irqreturn_t bq27x00_chg_isr(int irq, void *dev) { +static irqreturn_t bq27x00_chg_isr(int irq, void *dev) { struct power_supply *ps = (struct power_supply*) dev; - wake_lock_timeout(&chg_wake_lock, msecs_to_jiffies(500)); - /* dev_dbg(ps->dev, "%s: Updating battery status\n", __func__); Don't - do i/o in interrupt! */ + struct bq27x00_device_info *di = to_bq27x00_device_info(ps); + + olio_debug ("ISR triggered!\n"); - bq27x00_external_power_changed(ps); + wake_lock(&isr_wake_lock); + + cancel_delayed_work(&di->interrupt_work); + schedule_delayed_work(&di->interrupt_work, 0); return IRQ_HANDLED; } @@ -1476,6 +1633,18 @@ static int __init bq27x00_powersupply_init(struct bq27x00_device_info *di) di->bat.external_power_changed = bq27x00_external_power_changed; INIT_DELAYED_WORK(&di->work, bq27x00_battery_poll); + INIT_DELAYED_WORK(&di->interrupt_work, bq27x00_battery_interrupt); + +#ifdef CONFIG_OLIO_TS81001 + /* Set up alarm timer which wakes the device up if suspended. */ + + alarm_init (&di->poll_alarm, ALARM_BOOTTIME, + bq27x00_run_poll_alarm); + + di->alarm_time = ktime_set(BQ27X00_ALARM_TIMER_INTERVAL,0); + + alarm_start_relative (&di->poll_alarm, di->alarm_time); +#endif mutex_init(&di->lock); ret = power_supply_register(di->dev, &di->bat); @@ -1486,8 +1655,9 @@ static int __init bq27x00_powersupply_init(struct bq27x00_device_info *di) /* This device is a wakeup source */ - device_init_wakeup(di->dev, 1); + /* device_init_wakeup(di->dev, 1); */ + wake_lock_init(&isr_wake_lock, WAKE_LOCK_SUSPEND, "isr_wake_lock"); wake_lock_init(&chg_wake_lock, WAKE_LOCK_SUSPEND, "chg_wake_lock"); if(di->irq) { @@ -1502,6 +1672,7 @@ static int __init bq27x00_powersupply_init(struct bq27x00_device_info *di) } ret = enable_irq_wake(di->irq); + if (ret) { dev_err(di->dev, "failed to enable irq: %d, as wake\n", di->irq); return ret; @@ -1526,6 +1697,7 @@ static void bq27x00_powersupply_unregister(struct bq27x00_device_info *di) poll_interval = 0; cancel_delayed_work_sync(&di->work); + cancel_delayed_work_sync(&di->interrupt_work); power_supply_unregister(&di->bat); @@ -1763,6 +1935,7 @@ static const struct attribute_group bq27x00_attr_group = { #ifdef CONFIG_OLIO_TS81001 + /*************************************************************************** * bq27x00_set_platform_data - act on basic platform data * @@ -1891,6 +2064,7 @@ static int __init bq27x00_battery_probe(struct i2c_client *client, goto batt_failed_3; /* Schedule a polling after about 1 min */ + schedule_delayed_work(&di->work, 60 * HZ); i2c_set_clientdata(client, di); @@ -1944,8 +2118,8 @@ static struct i2c_driver bq27x00_battery_driver = { .driver = { .name = "bq27x00-battery", }, - .probe = bq27x00_battery_probe, - .remove = bq27x00_battery_remove, + .probe = bq27x00_battery_probe, + .remove = bq27x00_battery_remove, .id_table = bq27x00_id, }; diff --git a/drivers/staging/triune/ts81001.c b/drivers/staging/triune/ts81001.c index 9ede65febc9..ed6f8abc78f 100644 --- a/drivers/staging/triune/ts81001.c +++ b/drivers/staging/triune/ts81001.c @@ -48,10 +48,20 @@ /* Globals */ -const int TS81001_ADDR=0x49; -const u8 TS81001_STATUS_REG=0x10; -const u8 TS81001_CURRENT_REG=0x20; -const u16 TS81001_CHARGE_CURRENT=390; +const int TS81001_ADDR = 0x49; +const u8 TS81001_STATUS_REG = 0x10; +const u8 TS81001_CURRENT_REG_LSB = 0x20; +const u8 TS81001_CURRENT_REG_MSB = 0x21; + +/* Trying 400mA since we can't actually set 390mA. */ + +const u16 TS81001_CHARGE_CURRENT = 400; +const u8 TS81001_CHARGE_CURRENT_LSB = 0x90; /* 0x190 == 400 (0x186 == 390)*/ +const u8 TS81001_CHARGE_CURRENT_MSB = 0x01; + +const u8 TS81001_CHARGE_CURR_100_LSB = 0x64; /* 0x64 == 100 */ +const u8 TS81001_CHARGE_CURR_100_MSB = 0x00; + /*************************************************************************** @@ -66,7 +76,7 @@ static int ts81001_i2c_read (struct i2c_client * client, int err = 0; struct i2c_msg msg[2]; - olio_debug (" entered\n"); + olio_debug ("entered\n"); msg[0].addr = client->addr; msg[0].flags = client->flags; @@ -83,36 +93,42 @@ static int ts81001_i2c_read (struct i2c_client * client, else err = i2c_transfer(client->adapter, msg, 2); - olio_debug (" exiting\n"); + olio_debug ("exiting\n"); return err; } -static int ts81001_write_i2c_blk(struct i2c_client * client, u8 reg, - u8 *data, u8 sz) +static int ts81001_write_i2c_blk(struct i2c_client * client, u8 reg_addr, + u8 * data, u16 sz) { struct i2c_msg msg; int ret; - u8 buf[33]; + u8 buf[3]; + + olio_debug ("entered\n"); if (!client->adapter) return -ENODEV; - buf[0] = reg; + buf[0] = reg_addr; memcpy(&buf[1], data, sz); - msg.buf = buf; + olio_debug ("writing 0x%x,0x%x to 0x%x device 0x%x\n", + buf[1], buf[2], buf[0], client->addr); + msg.addr = client->addr; - msg.flags = 0; + msg.flags = 0; //client->flags; msg.len = sz + 1; + msg.buf = buf; ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) - return ret; + + olio_debug ("exiting, ret = %d\n", ret); - return 0; + return ret; } + /*************************************************************************** * ts81001_get_status - get current charger state. * @@ -132,10 +148,11 @@ static int ts81001_get_status (struct ts81001 * ts) { olio_debug ("entered\n"); for (i = 0; i < retries; i++) { - err = ts81001_i2c_read (ts->client, TS81001_STATUS_REG, len, &data, false); - + err = ts81001_i2c_read (ts->client, + TS81001_STATUS_REG, len, &data, false); if (err <= 0) { - olio_debug ("Error reading status from ts81001, err = %d\n", err); + olio_debug ("Error reading status from ts81001, err = %d\n", + err); state = err; } else { state = (ts81001_state_t) data; @@ -148,32 +165,237 @@ static int ts81001_get_status (struct ts81001 * ts) { return state; } -static int ts81001_request_charge (struct ts81001 * ts) { +/*************************************************************************** + * ts81001_request_charge - ask for current from charger + * + * This function allows us to set a new current for the charger. It depends + * on the firmware being recent enough, though. Returns the number of bytes + * written, or error (negative values) if needed. + */ + +static int ts81001_request_charge (struct ts81001 * ts, u16 mA) { int retries = 2; int err = 0; int i; - u16 request_current = TS81001_CHARGE_CURRENT; + /* u16 request_current = TS81001_CHARGE_CURRENT; */ + u8 data[2]; + + data[0] = mA & 0x00ff; + data[1] = (mA & 0xff00) >> 8; + + printk ("OLIO %s entered, requested current = lb:0x%x, hb:0x%x\n", + __FUNCTION__, data[0], data[1]); + + for (i = 0; i < retries; i++) { + err = ts81001_write_i2c_blk(ts->client, + TS81001_CURRENT_REG_LSB, + &data[0], 2); + if (err <= 0) + olio_debug ("error (%d) writing data to " + "register (try = %d)\n", err, i); + } + + return err; +} + +/*************************************************************************** + * ts81001_charging_stop - send charge complete to power provider + * + * This function allows to stop charging - stopping and restarting charging + * is intended to simulate the device taken off the charger and put back on. + */ + +#define TS81001_CHARGE_COMPLETE 0x01 +#define TS81001_STATE_REPORT_REG 0x12 + +static int ts81001_charging_stop (struct ts81001 * ts) { + u8 data; + int len = 1; + int err = 0; + int i, retries = 2; + + olio_debug ("entered\n"); + + data = TS81001_CHARGE_COMPLETE; + + for (i = 0; i < retries; i++) { + err = ts81001_write_i2c_blk(ts->client, TS81001_STATE_REPORT_REG, + (u8*) &data, len); + } + + if (err <= 0) { + olio_debug ("Error writing charging stop to ts81001, " + "err = %d\n", err); + } + + + olio_debug ("exiting\n"); + + return err; +} + + +/*************************************************************************** + * ts81001_charging_stop - send charge complete to power provider + * + * This function allows to stop charging - stopping and restarting charging + * is intended to simulate the device taken off the charger and put back on. + */ + +#define TS81001_CHARGE_RESTART 0xFF + +static int ts81001_charging_restart (struct ts81001 * ts) { + u8 data; + int len = 1; + int err = 0; + int i, retries = 2; + + olio_debug ("entered\n"); + + data = TS81001_CHARGE_RESTART; for (i = 0; i < retries; i++) { - err = ts81001_write_i2c_blk(ts->client, TS81001_CURRENT_REG, (u8*) &request_current, 2); + err = ts81001_write_i2c_blk(ts->client, + TS81001_STATE_REPORT_REG, + (u8*) &data, len); + + if (err <= 0) { + olio_debug ("Error writing state to ts81001, " + "err = %d\n", err); + } } + olio_debug ("exiting\n"); + return err; } +#if 0 + +/* NOT FUNCTIONAL, NOT SURE HOW TO MAKE IT FUNCTIONAL! */ + +/*************************************************************************** + * ts81001_disconnect - stop talking to power TX + * + * If we send a 'signal strength' package, the TX unit should forget that + * we've reached full charge. + */ + +static int ts81001_disconnect (struct ts81001 * ts) { + u8 data; + int len = 1; + int err = 0; + int i, retries = 2; + + olio_debug ("entered\n"); + + data = TS81001_CHARGE_RESTART; + + for (i = 0; i < retries; i++) { + err = ts81001_write_i2c_blk(ts->client, + TS81001_STATE_REPORT_REG, + (u8*) &data, len); + + if (err <= 0) { + olio_debug ("Error writing state to ts81001, " + "err = %d\n", err); + } + } + + olio_debug ("exiting\n"); + + return err; +} +#endif static struct ts81001_ops ts_ops = { - .get_status = ts81001_get_status, + .get_status = ts81001_get_status, .request_charge = ts81001_request_charge, + .charge_stop = ts81001_charging_stop, + .charge_start = ts81001_charging_restart, }; + + +/*************************************************************************** + * sysfs operations + ***************************************************************************/ + +/* sysfs entry for requesting power, mA's. */ + +/*************************************************************************** + * ts81001_sysfs_set_power_request - request power level from TX + * + * This entry allows us to request a certain power level from the TX unit. + * Mainly used for testing. + */ + +static ssize_t ts81001_sysfs_set_power_request ( + struct device * dev, + struct device_attribute * attr, + const char * buf, + size_t count) +{ + int mA_req; + int s; + u16 mA; + ssize_t retval = 0; + + struct ts81001 * ts81001 = + i2c_get_clientdata (to_i2c_client (dev)); + + if ((s = sscanf (buf, "%d\n", &mA_req)) != 1) { + printk ("%s OLIO wrong number of args (%d, expected 1) " + "for power request.\n", __FUNCTION__, s); + retval = -EINVAL; + goto done; + } else { + printk ("%s OLIO read %d for new power level\n", + __FUNCTION__, mA_req); + } + + if (mA_req > 0xffff) { + printk ("%s OLIO warning, max request is %d.\n", + __FUNCTION__, 0xffff); + mA_req = 0xffff; + } + + mA = (u16) mA_req; + + retval = ts81001_request_charge (ts81001, mA); + +done: + return count; +} + +/*************************************************************************** + * ts81001_sysfs_show_power_request - show the power level last requested + * + * This sysfs entry will read the value of the power request registers in + * the ts81001 and return it. + */ + +static ssize_t ts81001_sysfs_show_power_request (struct device * dev, + struct device_attribute * attr, + char * buf) { + + return 0; +} + +static struct device_attribute ts81001_device_attr = + __ATTR(ts81001_power_request, S_IWUGO | S_IRUGO, + ts81001_sysfs_show_power_request, + ts81001_sysfs_set_power_request); + + + /*************************************************************************** * probe */ static int ts81001_i2c_probe(struct i2c_client * client, - const struct i2c_device_id * id) + const struct i2c_device_id * id) { struct ts81001 *ts81001; @@ -198,6 +420,8 @@ static int ts81001_i2c_probe(struct i2c_client * client, i2c_set_clientdata(client, ts81001); + device_create_file (ts81001->dev, &ts81001_device_attr); + olio_debug ("exiting\n"); return ret; @@ -230,6 +454,6 @@ static struct i2c_driver ts81001_i2c_driver = { module_i2c_driver(ts81001_i2c_driver); -MODULE_AUTHOR("Mattis Fjallstrom <mattis@oliodevice.com"); +MODULE_AUTHOR("Mattis Fjallstrom <mattis@oliodevices.com"); MODULE_DESCRIPTION("Basic driver for TS81001"); MODULE_LICENSE("GPL"); diff --git a/drivers/staging/triune/ts81001.h b/drivers/staging/triune/ts81001.h index 8978ca2f0d1..aecf19780cc 100644 --- a/drivers/staging/triune/ts81001.h +++ b/drivers/staging/triune/ts81001.h @@ -52,7 +52,9 @@ struct ts81001 { struct ts81001_ops { int (*get_status) (struct ts81001 * ts); - int (*request_charge) (struct ts81001 * ts); + int (*request_charge) (struct ts81001 * ts, u16 charge); + int (*charge_stop) (struct ts81001 * ts); + int (*charge_start) (struct ts81001 * ts); }; #endif /* _CONFIG_TS81001_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 50ff7ca10a9..113ee1f928c 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -41,6 +41,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_DA732X if I2C select SND_SOC_DA9055 if I2C select SND_SOC_DFBMCS320 + select SND_SOC_OMAP3H1_BT_SCO select SND_SOC_ISABELLE if I2C select SND_SOC_JZ4740_CODEC select SND_SOC_LM4857 if I2C @@ -266,6 +267,9 @@ config SND_SOC_DA9055 config SND_SOC_DFBMCS320 tristate +config SND_SOC_OMAP3H1_BT_SCO + tristate + config SND_SOC_DMIC tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 39747d34c41..8e7bb4d4cbe 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -29,6 +29,7 @@ snd-soc-da7213-objs := da7213.o snd-soc-da732x-objs := da732x.o snd-soc-da9055-objs := da9055.o snd-soc-dfbmcs320-objs := dfbmcs320.o +snd-soc-omap3h1_bt_sco-objs := omap3h1_bt_sco.o snd-soc-dmic-objs := dmic.o snd-soc-isabelle-objs := isabelle.o snd-soc-jz4740-codec-objs := jz4740.o @@ -157,6 +158,7 @@ obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o obj-$(CONFIG_SND_SOC_DFBMCS320) += snd-soc-dfbmcs320.o +obj-$(CONFIG_SND_SOC_OMAP3H1_BT_SCO) += snd-soc-omap3h1_bt_sco.o obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o diff --git a/sound/soc/codecs/omap3h1_bt_sco.c b/sound/soc/codecs/omap3h1_bt_sco.c new file mode 100644 index 00000000000..f72f004f64c --- /dev/null +++ b/sound/soc/codecs/omap3h1_bt_sco.c @@ -0,0 +1,74 @@ +/* + * Driver for the DFBM-CS320 bluetooth module + * Copyright 2011 Lars-Peter Clausen <lars@metafoo.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <sound/soc.h> + +int omap3h1_bt_sco_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + printk ("OMAP3H1:ASoc %s:%s starting\n", __FILE__, __FUNCTION__); + return 0; +} + +void omap3h1_bt_sco_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + printk ("OMAP3H1:ASoc %s:%s shutting down\n", __FILE__, __FUNCTION__); +} + +static struct snd_soc_dai_ops omap3h1_bt_sco_dai_ops = { + .startup = omap3h1_bt_sco_dai_startup, + .shutdown = omap3h1_bt_sco_dai_shutdown, +}; + +static struct snd_soc_dai_driver omap3h1_bt_sco_dai = { + .name = "omap3h1_bt_sco-pcm", + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &omap3h1_bt_sco_dai_ops, +}; + +static struct snd_soc_codec_driver soc_codec_dev_omap3h1_bt_sco; + +static int omap3h1_bt_sco_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_omap3h1_bt_sco, + &omap3h1_bt_sco_dai, 1); +} + +static int omap3h1_bt_sco_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + + return 0; +} + + +static struct platform_driver omap3h1_bt_sco = { + .driver = { + .name = "omap3h1_bt_sco", + .owner = THIS_MODULE, + }, + .probe = omap3h1_bt_sco_probe, + .remove = omap3h1_bt_sco_remove, +}; + +module_platform_driver(omap3h1_bt_sco); + +MODULE_AUTHOR("Evan Wilson <evan@oliodevices.com"); +MODULE_DESCRIPTION("OMAP3 H1 ALSA SoC codec driver"); +MODULE_LICENSE("GPL");
\ No newline at end of file diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 66d98e26a24..08c94b19bb9 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -31,6 +31,7 @@ config SND_OMAP_SOC_OMAP3_H1 depends on SND_OMAP_SOC && MACH_OMAP3_H1 select SND_OMAP_SOC_MCBSP select SND_SOC_DMIC + select SND_SOC_OMAP3H1_BT_SCO help Say Y if you want to add support for SoC audio on the Olio H1 board. diff --git a/sound/soc/omap/omap3h1.c b/sound/soc/omap/omap3h1.c index f61949b295e..faad74810c3 100644 --- a/sound/soc/omap/omap3h1.c +++ b/sound/soc/omap/omap3h1.c @@ -22,7 +22,7 @@ * */ -#define DEBUG +//#define DEBUG #include <linux/clk.h> #include <linux/gpio.h> #include <linux/of_gpio.h> @@ -42,19 +42,22 @@ static struct clk *per_96m_fck; static unsigned long rate; static int mic_gpio_enable; -static int omap3h1_hw_params(struct snd_pcm_substream *substream, +static int omap3h1_hw_in_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - //unsigned int fmt; int ret = 0; - unsigned int freq; - //freq = 256 * params_rate(params); - // We are triggering from the 96 MHz FSCK + pr_info("ASoc OMAP3H1: omap3h1_hw_in_params\n"); + pr_info("ASoc OMAP3H1: in params channels=%d\n", params_channels(params)); + pr_info("ASoc OMAP3H1: in params rate=%d\n", params_rate(params)); + pr_info("ASoc OMAP3H1: in params params_period_size=%d\n", params_period_size(params)); + pr_info("ASoc OMAP3H1: in params params_periods=%d\n", params_periods(params)); + pr_info("ASoc OMAP3H1: in params params_buffer_size=%d\n", params_buffer_size(params)); + pr_info("ASoc OMAP3H1: in params params_buffer_bytes=%d\n", params_buffer_bytes(params)); - // pr_info("ASoc OMAP3H1: setting system clock to: %d", freq); + /* Set McBSP clock to use PER_96M_FCLK */ ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_FCLK, rate, SND_SOC_CLOCK_OUT); if (ret < 0) { @@ -62,68 +65,121 @@ static int omap3h1_hw_params(struct snd_pcm_substream *substream, return ret; } - // Set the divider so that our clock rate is ~3KHz, this is about - // 48KHz sampling frequency on the mic (32-bit/channel, 2 channels, 48 - // Khz sampling) + /* The microphone becomes operational 2^18 clock cycles */ + /* (85 ms with SCK at 3.072 MHz) */ ret = snd_soc_dai_set_clkdiv(cpu_dai, OMAP_MCBSP_CLKGDV, 32); if (ret < 0) { - pr_err("ASoc OMAP3H1: can't set SRG clock divider\n"); + pr_err("ASoc OMAP3H1: can't set MIC clock divider\n"); return ret; } return 0; } -static int omap3h1_startup(struct snd_pcm_substream *substream) +static int omap3h1_hw_out_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + + pr_info("ASoc OMAP3H1: omap3h1_hw_out_params"); + pr_info("ASoc OMAP3H1: out params channels=%u\n", params_channels(params)); + pr_info("ASoc OMAP3H1: out params rate=%u\n", params_rate(params)); + pr_info("ASoc OMAP3H1: out params params_period_size=%u\n", params_period_size(params)); + pr_info("ASoc OMAP3H1: out params params_periods=%u\n", params_periods(params)); + pr_info("ASoc OMAP3H1: out params params_buffer_size=%u\n", params_buffer_size(params)); + pr_info("ASoc OMAP3H1: out params params_buffer_bytes=%u\n", params_buffer_bytes(params)); + + /* Set McBSP clock to use PER_96M_FCLK */ + ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_FCLK, + rate, SND_SOC_CLOCK_OUT); + if (ret < 0) { + printk(KERN_ERR "can't set DMIC cpu system clock\n"); + return ret; + } + + /* The BT operates on 512K freq */ + ret = snd_soc_dai_set_clkdiv(cpu_dai, OMAP_MCBSP_CLKGDV, rate/512000); + if (ret < 0) { + pr_err("ASoc OMAP3H1: can't set BT clock divider\n"); + return ret; + } + + return 0; +} + + +static int omap3h1_in_startup(struct snd_pcm_substream *substream) { + pr_info("ASoc OMAP3H1: omap3h1_in_startup"); gpio_set_value(mic_gpio_enable, 1); return clk_enable(per_96m_fck); } -static void omap3h1_shutdown(struct snd_pcm_substream *substream) +static void omap3h1_in_shutdown(struct snd_pcm_substream *substream) { + pr_info("ASoc OMAP3H1: omap3h1_in_shutdown"); gpio_set_value(mic_gpio_enable, 0); clk_disable(per_96m_fck); } -static struct snd_soc_ops omap3h1_ops = { - .hw_params = omap3h1_hw_params, - .startup = omap3h1_startup, - .shutdown = omap3h1_shutdown, +static int omap3h1_out_startup(struct snd_pcm_substream *substream) +{ + pr_info("ASoc OMAP3H1: omap3h1_out_startup"); + return clk_enable(per_96m_fck); +} + +static void omap3h1_out_shutdown(struct snd_pcm_substream *substream) +{ + pr_info("ASoc OMAP3H1: omap3h1_out_shutdown"); + clk_disable(per_96m_fck); +} + +static struct snd_soc_ops omap3h1_in_ops = { + .hw_params = omap3h1_hw_in_params, + .startup = omap3h1_in_startup, + .shutdown = omap3h1_in_shutdown, }; -//static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = { -// SND_SOC_DAPM_MIC("Digital Mic", NULL), -//}; -// -//static const struct snd_soc_dapm_route dmic_audio_map[] = { -// {"DMic", NULL, "Digital Mic"}, -//}; +static struct snd_soc_ops omap3h1_out_ops = { + .hw_params = omap3h1_hw_out_params, + .startup = omap3h1_out_startup, + .shutdown = omap3h1_out_shutdown, +}; /* Digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link omap3h1_dai = { - .name = "DMIC", - .stream_name = "DMIC Capture", - .cpu_dai_name = "omap-mcbsp.3", - .codec_dai_name = "dmic-hifi", - .platform_name = "omap-pcm-audio", - .codec_name = "dmic-codec", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .ops = &omap3h1_ops, +static struct snd_soc_dai_link omap3h1_dai[] = { + { + .name = "DMIC", + .stream_name = "DMIC Capture", + .cpu_dai_name = "omap-mcbsp.3", + .codec_dai_name = "dmic-hifi", + .platform_name = "omap-pcm-audio", + .codec_name = "dmic-codec", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &omap3h1_in_ops, + }, + { + .name = "BT", + .stream_name = "BT Playback", + .cpu_dai_name = "omap-mcbsp.2", + .codec_dai_name = "omap3h1_bt_sco-pcm", + .platform_name = "omap-pcm-audio", + .codec_name = "omap3h1_bt_sco", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &omap3h1_out_ops, + } }; /* Audio machine driver */ static struct snd_soc_card snd_soc_omap3h1 = { .name = "omap3h1", .owner = THIS_MODULE, - .dai_link = &omap3h1_dai, - .num_links = 1, - -// .dapm_widgets = dmic_dapm_widgets, -// .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets), -// .dapm_routes = dmic_audio_map, -// .num_dapm_routes = ARRAY_SIZE(dmic_audio_map), + .dai_link = omap3h1_dai, + .num_links = ARRAY_SIZE(omap3h1_dai), }; static int omap3h1_card_probe(struct platform_device *pdev) @@ -135,15 +191,24 @@ static int omap3h1_card_probe(struct platform_device *pdev) #ifdef CONFIG_OF if (node) { - struct device_node *dai_node; + struct device_node *dai_node1; + struct device_node *dai_node2; + + dai_node1 = of_parse_phandle(node, "olio,mcbsp_mic", 0); + if (!dai_node1) { + dev_err(&pdev->dev, "mcbsp3 node is not provided\n"); + return -EINVAL; + } + omap3h1_dai[0].cpu_dai_name = NULL; + omap3h1_dai[0].cpu_of_node = dai_node1; - dai_node = of_parse_phandle(node, "olio,mcbsp", 0); - if (!dai_node) { - dev_err(&pdev->dev, "mcbsp node is not provided\n"); + dai_node2 = of_parse_phandle(node, "olio,mcbsp_bt", 0); + if (!dai_node2) { + dev_err(&pdev->dev, "mcbsp2 node is not provided\n"); return -EINVAL; } - omap3h1_dai.cpu_dai_name = NULL; - omap3h1_dai.cpu_of_node = dai_node; + omap3h1_dai[1].cpu_dai_name = NULL; + omap3h1_dai[1].cpu_of_node = dai_node2; } else { dev_err(&pdev->dev, "Missing node\n"); @@ -191,52 +256,18 @@ static int omap3h1_card_remove(struct platform_device *pdev) return 0; } -/** - * omap3h1_sound_suspend - suspend microphone - * - */ - -static int omap3h1_sound_suspend(struct device *dev) { - printk ("OLIO %s:%s Suspending\n", __FILE__, __FUNCTION__); - - snd_soc_suspend(dev); - - return 0; -} - - -/** - * omap3h1_sound_resume - resume microphone - * - */ - -static int omap3h1_sound_resume(struct device *dev) { - printk ("OLIO %s:%s Resuming\n", __FILE__, __FUNCTION__); - - snd_soc_resume (dev); - - return 0; -} - - -/* ---------------------------------------------------------------------- */ - -static const struct dev_pm_ops omap3h1_sound_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(omap3h1_sound_suspend, omap3h1_sound_resume) -}; - - static const struct of_device_id omap_soc_h1_of_match[] = { {.compatible = "olio,omap-soc-omap3h1", }, { }, }; + MODULE_DEVICE_TABLE(of, omap_soc_h1_of_match); static struct platform_driver omap3h1_driver = { .driver = { .name = "omap-soc-omap3h1", .of_match_table = of_match_ptr(omap_soc_h1_of_match), - .pm = &snd_soc_pm_ops, + .pm = &snd_soc_pm_ops, }, .probe = omap3h1_card_probe, .remove = omap3h1_card_remove, |