/* Force full reconfiguration when switching between PLL */ if (pll_id != twl6040->pll) {
twl6040->sysclk_rate = 0;
twl6040->mclk_rate = 0;
}
switch (pll_id) { case TWL6040_SYSCLK_SEL_LPPLL: /* low-power PLL divider */ /* Change the sysclk configuration only if it has been canged */ if (twl6040->sysclk_rate != freq_out) { switch (freq_out) { case 17640000:
lppllctl |= TWL6040_LPLLFIN; break; case 19200000:
lppllctl &= ~TWL6040_LPLLFIN; break; default:
dev_err(twl6040->dev, "freq_out %d not supported\n",
freq_out);
ret = -EINVAL; goto pll_out;
}
twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL,
lppllctl);
}
/* The PLL in use has not been change, we can exit */ if (twl6040->pll == pll_id) break;
clk_disable_unprepare(twl6040->mclk); break; case TWL6040_SYSCLK_SEL_HPPLL: /* high-performance PLL can provide only 19.2 MHz */ if (freq_out != 19200000) {
dev_err(twl6040->dev, "freq_out %d not supported\n", freq_out);
ret = -EINVAL; goto pll_out;
}
if (twl6040->mclk_rate != freq_in) {
hppllctl &= ~TWL6040_MCLK_MSK;
switch (freq_in) { case 12000000: /* PLL enabled, active mode */
hppllctl |= TWL6040_MCLK_12000KHZ |
TWL6040_HPLLENA; break; case 19200000: /* PLL enabled, bypass mode */
hppllctl |= TWL6040_MCLK_19200KHZ |
TWL6040_HPLLBP | TWL6040_HPLLENA; break; case 26000000: /* PLL enabled, active mode */
hppllctl |= TWL6040_MCLK_26000KHZ |
TWL6040_HPLLENA; break; case 38400000: /* PLL enabled, bypass mode */
hppllctl |= TWL6040_MCLK_38400KHZ |
TWL6040_HPLLBP | TWL6040_HPLLENA; break; default:
dev_err(twl6040->dev, "freq_in %d not supported\n", freq_in);
ret = -EINVAL; goto pll_out;
}
/* When switching to HPPLL, enable the mclk first */ if (pll_id != twl6040->pll)
clk_prepare_enable(twl6040->mclk); /* * enable clock slicer to ensure input waveform is * square
*/
hppllctl |= TWL6040_HPLLSQRENA;
/* Get the combined status of the vibra control register */ int twl6040_get_vibralr_status(struct twl6040 *twl6040)
{ unsignedint reg; int ret;
u8 status;
ret = regmap_read(twl6040->regmap, TWL6040_REG_VIBCTLL, ®); if (ret != 0) return ret;
status = reg;
ret = regmap_read(twl6040->regmap, TWL6040_REG_VIBCTLR, ®); if (ret != 0) return ret;
status |= reg;
staticbool twl6040_readable_reg(struct device *dev, unsignedint reg)
{ /* Register 0 is not readable */ if (!reg) returnfalse; returntrue;
}
staticbool twl6040_volatile_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case TWL6040_REG_ASICID: case TWL6040_REG_ASICREV: case TWL6040_REG_INTID: case TWL6040_REG_LPPLLCTL: case TWL6040_REG_HPPLLCTL: case TWL6040_REG_STATUS: returntrue; default: returnfalse;
}
}
staticbool twl6040_writeable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case TWL6040_REG_ASICID: case TWL6040_REG_ASICREV: case TWL6040_REG_STATUS: returnfalse; default: returntrue;
}
}
if (!node) {
dev_err(&client->dev, "of node is missing\n"); return -EINVAL;
}
/* In order to operate correctly we need valid interrupt config */ if (!client->irq) {
dev_err(&client->dev, "Invalid IRQ configuration\n"); return -EINVAL;
}
twl6040 = devm_kzalloc(&client->dev, sizeof(struct twl6040),
GFP_KERNEL); if (!twl6040) return -ENOMEM;
twl6040->regmap = devm_regmap_init_i2c(client, &twl6040_regmap_config); if (IS_ERR(twl6040->regmap)) return PTR_ERR(twl6040->regmap);
i2c_set_clientdata(client, twl6040);
twl6040->clk32k = devm_clk_get(&client->dev, "clk32k"); if (IS_ERR(twl6040->clk32k)) { if (PTR_ERR(twl6040->clk32k) == -EPROBE_DEFER) return -EPROBE_DEFER;
dev_dbg(&client->dev, "clk32k is not handled\n");
twl6040->clk32k = NULL;
}
twl6040->mclk = devm_clk_get(&client->dev, "mclk"); if (IS_ERR(twl6040->mclk)) { if (PTR_ERR(twl6040->mclk) == -EPROBE_DEFER) return -EPROBE_DEFER;
dev_dbg(&client->dev, "mclk is not handled\n");
twl6040->mclk = NULL;
}
twl6040->supplies[0].supply = "vio";
twl6040->supplies[1].supply = "v2v1";
ret = devm_regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES,
twl6040->supplies); if (ret != 0) {
dev_err(&client->dev, "Failed to get supplies: %d\n", ret); return ret;
}
ret = regulator_bulk_enable(TWL6040_NUM_SUPPLIES, twl6040->supplies); if (ret != 0) {
dev_err(&client->dev, "Failed to enable supplies: %d\n", ret); return ret;
}
twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV); if (twl6040->rev < 0) {
dev_err(&client->dev, "Failed to read revision register: %d\n",
twl6040->rev);
ret = twl6040->rev; goto gpio_err;
}
/* ERRATA: Automatic power-up is not possible in ES1.0 */ if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) {
twl6040->audpwron = devm_gpiod_get_optional(&client->dev, "ti,audpwron",
GPIOD_OUT_LOW);
ret = PTR_ERR_OR_ZERO(twl6040->audpwron); if (ret) goto gpio_err;
/* * The main functionality of twl6040 to provide audio on OMAP4+ systems. * We can add the ASoC codec child whenever this driver has been loaded.
*/
irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_PLUG);
cell = &twl6040->cells[children];
cell->name = "twl6040-codec";
twl6040_codec_rsrc[0].start = irq;
twl6040_codec_rsrc[0].end = irq;
cell->resources = twl6040_codec_rsrc;
cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc);
children++;
/* Vibra input driver support */ if (twl6040_has_vibra(node)) {
irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_VIB);
/* The chip is powered down so mark regmap to cache only and dirty */
regcache_cache_only(twl6040->regmap, true);
regcache_mark_dirty(twl6040->regmap);
ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children,
NULL, 0, NULL); if (ret) goto readyirq_err;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.