brintos

brintos / linux-shallow public Read only

0
0
Text · 15.7 KiB · 28c92d9 Raw
636 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2//3// Codec driver for Microsemi ZL38060 Connected Home Audio Processor.4//5// Copyright(c) 2020 Sven Van Asbroeck6 7// The ZL38060 is very flexible and configurable. This driver implements only a8// tiny subset of the chip's possible configurations:9//10// - DSP block bypassed: DAI        routed straight to DACs11//                       microphone routed straight to DAI12// - chip's internal clock is driven by a 12 MHz external crystal13// - chip's DAI connected to CPU is I2S, and bit + frame clock master14// - chip must be strapped for "host boot": in this mode, firmware will be15//   provided by this driver.16 17#include <linux/gpio/consumer.h>18#include <linux/gpio/driver.h>19#include <linux/property.h>20#include <linux/spi/spi.h>21#include <linux/regmap.h>22#include <linux/module.h>23#include <linux/ihex.h>24 25#include <sound/pcm_params.h>26#include <sound/core.h>27#include <sound/pcm.h>28#include <sound/soc.h>29 30#define DRV_NAME		"zl38060"31 32#define ZL38_RATES		(SNDRV_PCM_RATE_8000  |\33				SNDRV_PCM_RATE_16000 |\34				SNDRV_PCM_RATE_48000)35#define ZL38_FORMATS		SNDRV_PCM_FMTBIT_S16_LE36 37#define HBI_FIRMWARE_PAGE	0xFF38#define ZL38_MAX_RAW_XFER	0x10039 40#define REG_TDMA_CFG_CLK	0x026241#define CFG_CLK_PCLK_SHIFT	442#define CFG_CLK_PCLK_MASK	(0x7ff << CFG_CLK_PCLK_SHIFT)43#define CFG_CLK_PCLK(bits)	((bits - 1) << CFG_CLK_PCLK_SHIFT)44#define CFG_CLK_MASTER		BIT(15)45#define CFG_CLK_FSRATE_MASK	0x746#define CFG_CLK_FSRATE_8KHZ	0x147#define CFG_CLK_FSRATE_16KHZ	0x248#define CFG_CLK_FSRATE_48KHZ	0x649 50#define REG_CLK_CFG		0x001651#define CLK_CFG_SOURCE_XTAL	BIT(15)52 53#define REG_CLK_STATUS		0x001454#define CLK_STATUS_HWRST	BIT(0)55 56#define REG_PARAM_RESULT	0x003457#define PARAM_RESULT_READY	0xD3D358 59#define REG_PG255_BASE_HI	0x000C60#define REG_PG255_OFFS(addr)	((HBI_FIRMWARE_PAGE << 8) | (addr & 0xFF))61#define REG_FWR_EXEC		0x012C62 63#define REG_CMD			0x003264#define REG_HW_REV		0x002065#define REG_FW_PROD		0x002266#define REG_FW_REV		0x002467 68#define REG_SEMA_FLAGS		0x000669#define SEMA_FLAGS_BOOT_CMD	BIT(0)70#define SEMA_FLAGS_APP_REBOOT	BIT(1)71 72#define REG_HW_REV		0x002073#define REG_FW_PROD		0x002274#define REG_FW_REV		0x002475#define REG_GPIO_DIR		0x02DC76#define REG_GPIO_DAT		0x02DA77 78#define BOOTCMD_LOAD_COMPLETE	0x000D79#define BOOTCMD_FW_GO		0x000880 81#define FIRMWARE_MAJOR		282#define FIRMWARE_MINOR		283 84struct zl38_codec_priv {85	struct device *dev;86	struct regmap *regmap;87	bool is_stream_in_use[2];88	struct gpio_chip *gpio_chip;89};90 91static int zl38_fw_issue_command(struct regmap *regmap, u16 cmd)92{93	unsigned int val;94	int err;95 96	err = regmap_read_poll_timeout(regmap, REG_SEMA_FLAGS, val,97				       !(val & SEMA_FLAGS_BOOT_CMD), 10000,98				       10000 * 100);99	if (err)100		return err;101	err = regmap_write(regmap, REG_CMD, cmd);102	if (err)103		return err;104	err = regmap_update_bits(regmap, REG_SEMA_FLAGS, SEMA_FLAGS_BOOT_CMD,105				 SEMA_FLAGS_BOOT_CMD);106	if (err)107		return err;108 109	return regmap_read_poll_timeout(regmap, REG_CMD, val, !val, 10000,110					10000 * 100);111}112 113static int zl38_fw_go(struct regmap *regmap)114{115	int err;116 117	err = zl38_fw_issue_command(regmap, BOOTCMD_LOAD_COMPLETE);118	if (err)119		return err;120 121	return zl38_fw_issue_command(regmap, BOOTCMD_FW_GO);122}123 124static int zl38_fw_enter_boot_mode(struct regmap *regmap)125{126	unsigned int val;127	int err;128 129	err = regmap_update_bits(regmap, REG_CLK_STATUS, CLK_STATUS_HWRST,130				 CLK_STATUS_HWRST);131	if (err)132		return err;133 134	return regmap_read_poll_timeout(regmap, REG_PARAM_RESULT, val,135					val == PARAM_RESULT_READY, 1000, 50000);136}137 138static int139zl38_fw_send_data(struct regmap *regmap, u32 addr, const void *data, u16 len)140{141	__be32 addr_base = cpu_to_be32(addr & ~0xFF);142	int err;143 144	err = regmap_raw_write(regmap, REG_PG255_BASE_HI, &addr_base,145			       sizeof(addr_base));146	if (err)147		return err;148	return regmap_raw_write(regmap, REG_PG255_OFFS(addr), data, len);149}150 151static int zl38_fw_send_xaddr(struct regmap *regmap, const void *data)152{153	/* execution address from ihex: 32-bit little endian.154	 * device register expects 32-bit big endian.155	 */156	u32 addr = le32_to_cpup(data);157	__be32 baddr = cpu_to_be32(addr);158 159	return regmap_raw_write(regmap, REG_FWR_EXEC, &baddr, sizeof(baddr));160}161 162static int zl38_load_firmware(struct device *dev, struct regmap *regmap)163{164	const struct ihex_binrec *rec;165	const struct firmware *fw;166	u32 addr;167	u16 len;168	int err;169 170	/* how to get this firmware:171	 * 1. request and download chip firmware from Microsemi172	 *    (provided by Microsemi in srec format)173	 * 2. convert downloaded firmware from srec to ihex. Simple tool:174	 *    https://gitlab.com/TheSven73/s3-to-irec175	 * 3. convert ihex to binary (.fw) using ihex2fw tool which is included176	 *    with the Linux kernel sources177	 */178	err = request_ihex_firmware(&fw, "zl38060.fw", dev);179	if (err)180		return err;181	err = zl38_fw_enter_boot_mode(regmap);182	if (err)183		goto out;184	rec = (const struct ihex_binrec *)fw->data;185	while (rec) {186		addr = be32_to_cpu(rec->addr);187		len = be16_to_cpu(rec->len);188		if (addr) {189			/* regular data ihex record */190			err = zl38_fw_send_data(regmap, addr, rec->data, len);191		} else if (len == 4) {192			/* execution address ihex record */193			err = zl38_fw_send_xaddr(regmap, rec->data);194		} else {195			err = -EINVAL;196		}197		if (err)198			goto out;199		/* next ! */200		rec = ihex_next_binrec(rec);201	}202	err = zl38_fw_go(regmap);203 204out:205	release_firmware(fw);206	return err;207}208 209 210static int zl38_software_reset(struct regmap *regmap)211{212	unsigned int val;213	int err;214 215	err = regmap_update_bits(regmap, REG_SEMA_FLAGS, SEMA_FLAGS_APP_REBOOT,216				 SEMA_FLAGS_APP_REBOOT);217	if (err)218		return err;219 220	/* wait for host bus interface to settle.221	 * Not sure if this is required: Microsemi's vendor driver does this,222	 * but the firmware manual does not mention it. Leave it in, there's223	 * little downside, apart from a slower reset.224	 */225	msleep(50);226 227	return regmap_read_poll_timeout(regmap, REG_SEMA_FLAGS, val,228					!(val & SEMA_FLAGS_APP_REBOOT), 10000,229					10000 * 100);230}231 232static int zl38_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)233{234	struct zl38_codec_priv *priv = snd_soc_dai_get_drvdata(dai);235	int err;236 237	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {238	case SND_SOC_DAIFMT_I2S:239		/* firmware default is normal i2s */240		break;241	default:242		return -EINVAL;243	}244 245	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {246	case SND_SOC_DAIFMT_NB_NF:247		/* firmware default is normal bitclock and frame */248		break;249	default:250		return -EINVAL;251	}252 253	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {254	case SND_SOC_DAIFMT_CBP_CFP:255		/* always 32 bits per frame (= 16 bits/channel, 2 channels) */256		err = regmap_update_bits(priv->regmap, REG_TDMA_CFG_CLK,257					 CFG_CLK_MASTER | CFG_CLK_PCLK_MASK,258					 CFG_CLK_MASTER | CFG_CLK_PCLK(32));259		if (err)260			return err;261		break;262	default:263		return -EINVAL;264	}265 266	return 0;267}268 269static int zl38_hw_params(struct snd_pcm_substream *substream,270			  struct snd_pcm_hw_params *params,271			  struct snd_soc_dai *dai)272{273	struct zl38_codec_priv *priv = snd_soc_dai_get_drvdata(dai);274	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;275	unsigned int fsrate;276	int err;277 278	/* We cannot change hw_params while the dai is already in use - the279	 * software reset will corrupt the audio. However, this is not required,280	 * as the chip's TDM buses are fully symmetric, which mandates identical281	 * rates, channels, and samplebits for record and playback.282	 */283	if (priv->is_stream_in_use[!tx])284		goto skip_setup;285 286	switch (params_rate(params)) {287	case 8000:288		fsrate = CFG_CLK_FSRATE_8KHZ;289		break;290	case 16000:291		fsrate = CFG_CLK_FSRATE_16KHZ;292		break;293	case 48000:294		fsrate = CFG_CLK_FSRATE_48KHZ;295		break;296	default:297		return -EINVAL;298	}299 300	err = regmap_update_bits(priv->regmap, REG_TDMA_CFG_CLK,301				 CFG_CLK_FSRATE_MASK, fsrate);302	if (err)303		return err;304 305	/* chip requires a software reset to apply audio register changes */306	err = zl38_software_reset(priv->regmap);307	if (err)308		return err;309 310skip_setup:311	priv->is_stream_in_use[tx] = true;312 313	return 0;314}315 316static int zl38_hw_free(struct snd_pcm_substream *substream,317			struct snd_soc_dai *dai)318{319	struct zl38_codec_priv *priv = snd_soc_dai_get_drvdata(dai);320	bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;321 322	priv->is_stream_in_use[tx] = false;323 324	return 0;325}326 327/* stereo bypass with no AEC */328static const struct reg_sequence cp_config_stereo_bypass[] = {329	/* interconnects must be programmed first */330	{ 0x0210, 0x0005 },	/* DAC1   in <= I2S1-L */331	{ 0x0212, 0x0006 },	/* DAC2   in <= I2S1-R */332	{ 0x0214, 0x0001 },	/* I2S1-L in <= MIC1   */333	{ 0x0216, 0x0001 },	/* I2S1-R in <= MIC1   */334	{ 0x0224, 0x0000 },	/* AEC-S  in <= n/a    */335	{ 0x0226, 0x0000 },	/* AEC-R  in <= n/a    */336	/* output enables must be programmed next */337	{ 0x0202, 0x000F },	/* enable I2S1 + DAC   */338};339 340static const struct snd_soc_dai_ops zl38_dai_ops = {341	.set_fmt = zl38_set_fmt,342	.hw_params = zl38_hw_params,343	.hw_free = zl38_hw_free,344};345 346static struct snd_soc_dai_driver zl38_dai = {347	.name = "zl38060-tdma",348	.playback = {349		.stream_name = "Playback",350		.channels_min = 2,351		.channels_max = 2,352		.rates = ZL38_RATES,353		.formats = ZL38_FORMATS,354	},355	.capture = {356		.stream_name = "Capture",357		.channels_min = 2,358		.channels_max = 2,359		.rates = ZL38_RATES,360		.formats = ZL38_FORMATS,361	},362	.ops = &zl38_dai_ops,363	.symmetric_rate = 1,364	.symmetric_sample_bits = 1,365	.symmetric_channels = 1,366};367 368static const struct snd_soc_dapm_widget zl38_dapm_widgets[] = {369	SND_SOC_DAPM_OUTPUT("DAC1"),370	SND_SOC_DAPM_OUTPUT("DAC2"),371 372	SND_SOC_DAPM_INPUT("DMICL"),373};374 375static const struct snd_soc_dapm_route zl38_dapm_routes[] = {376	{ "DAC1",  NULL, "Playback" },377	{ "DAC2",  NULL, "Playback" },378 379	{ "Capture",  NULL, "DMICL" },380};381 382static const struct snd_soc_component_driver zl38_component_dev = {383	.dapm_widgets		= zl38_dapm_widgets,384	.num_dapm_widgets	= ARRAY_SIZE(zl38_dapm_widgets),385	.dapm_routes		= zl38_dapm_routes,386	.num_dapm_routes	= ARRAY_SIZE(zl38_dapm_routes),387	.endianness		= 1,388};389 390static void chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val)391{392	struct regmap *regmap = gpiochip_get_data(c);393	unsigned int mask = BIT(offset);394 395	regmap_update_bits(regmap, REG_GPIO_DAT, mask, val ? mask : 0);396}397 398static int chip_gpio_get(struct gpio_chip *c, unsigned int offset)399{400	struct regmap *regmap = gpiochip_get_data(c);401	unsigned int mask = BIT(offset);402	unsigned int val;403	int err;404 405	err = regmap_read(regmap, REG_GPIO_DAT, &val);406	if (err)407		return err;408 409	return !!(val & mask);410}411 412static int chip_direction_input(struct gpio_chip *c, unsigned int offset)413{414	struct regmap *regmap = gpiochip_get_data(c);415	unsigned int mask = BIT(offset);416 417	return regmap_update_bits(regmap, REG_GPIO_DIR, mask, 0);418}419 420static int421chip_direction_output(struct gpio_chip *c, unsigned int offset, int val)422{423	struct regmap *regmap = gpiochip_get_data(c);424	unsigned int mask = BIT(offset);425 426	chip_gpio_set(c, offset, val);427	return regmap_update_bits(regmap, REG_GPIO_DIR, mask, mask);428}429 430static const struct gpio_chip template_chip = {431	.owner = THIS_MODULE,432	.label = DRV_NAME,433 434	.base = -1,435	.ngpio = 14,436	.direction_input = chip_direction_input,437	.direction_output = chip_direction_output,438	.get = chip_gpio_get,439	.set = chip_gpio_set,440 441	.can_sleep = true,442};443 444static int zl38_check_revision(struct device *dev, struct regmap *regmap)445{446	unsigned int hwrev, fwprod, fwrev;447	int fw_major, fw_minor, fw_micro;448	int err;449 450	err = regmap_read(regmap, REG_HW_REV, &hwrev);451	if (err)452		return err;453	err = regmap_read(regmap, REG_FW_PROD, &fwprod);454	if (err)455		return err;456	err = regmap_read(regmap, REG_FW_REV, &fwrev);457	if (err)458		return err;459 460	fw_major = (fwrev >> 12) & 0xF;461	fw_minor = (fwrev >>  8) & 0xF;462	fw_micro = fwrev & 0xFF;463	dev_info(dev, "hw rev 0x%x, fw product code %d, firmware rev %d.%d.%d",464		 hwrev & 0x1F, fwprod, fw_major, fw_minor, fw_micro);465 466	if (fw_major != FIRMWARE_MAJOR || fw_minor < FIRMWARE_MINOR) {467		dev_err(dev, "unsupported firmware. driver supports %d.%d",468			FIRMWARE_MAJOR, FIRMWARE_MINOR);469		return -EINVAL;470	}471 472	return 0;473}474 475static int zl38_bus_read(void *context,476			 const void *reg_buf, size_t reg_size,477			 void *val_buf, size_t val_size)478{479	struct spi_device *spi = context;480	const u8 *reg_buf8 = reg_buf;481	size_t len = 0;482	u8 offs, page;483	u8 txbuf[4];484 485	if (reg_size != 2 || val_size > ZL38_MAX_RAW_XFER)486		return -EINVAL;487 488	offs = reg_buf8[1] >> 1;489	page = reg_buf8[0];490 491	if (page) {492		txbuf[len++] = 0xFE;493		txbuf[len++] = page == HBI_FIRMWARE_PAGE ? 0xFF : page - 1;494		txbuf[len++] = offs;495		txbuf[len++] = val_size / 2 - 1;496	} else {497		txbuf[len++] = offs | 0x80;498		txbuf[len++] = val_size / 2 - 1;499	}500 501	return spi_write_then_read(spi, txbuf, len, val_buf, val_size);502}503 504static int zl38_bus_write(void *context, const void *data, size_t count)505{506	struct spi_device *spi = context;507	u8 buf[4 + ZL38_MAX_RAW_XFER];508	size_t val_len, len = 0;509	const u8 *data8 = data;510	u8 offs, page;511 512	if (count > (2 + ZL38_MAX_RAW_XFER) || count < 4)513		return -EINVAL;514	val_len = count - 2;515	offs = data8[1] >> 1;516	page = data8[0];517 518	if (page) {519		buf[len++] = 0xFE;520		buf[len++] = page == HBI_FIRMWARE_PAGE ? 0xFF : page - 1;521		buf[len++] = offs;522		buf[len++] = (val_len / 2 - 1) | 0x80;523	} else {524		buf[len++] = offs | 0x80;525		buf[len++] = (val_len / 2 - 1) | 0x80;526	}527	memcpy(buf + len, data8 + 2, val_len);528	len += val_len;529 530	return spi_write(spi, buf, len);531}532 533static const struct regmap_bus zl38_regmap_bus = {534	.read = zl38_bus_read,535	.write = zl38_bus_write,536	.max_raw_write = ZL38_MAX_RAW_XFER,537	.max_raw_read = ZL38_MAX_RAW_XFER,538};539 540static const struct regmap_config zl38_regmap_conf = {541	.reg_bits = 16,542	.val_bits = 16,543	.reg_stride = 2,544	.use_single_read = true,545	.use_single_write = true,546};547 548static int zl38_spi_probe(struct spi_device *spi)549{550	struct device *dev = &spi->dev;551	struct zl38_codec_priv *priv;552	struct gpio_desc *reset_gpio;553	int err;554 555	/* get the chip to a known state by putting it in reset */556	reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);557	if (IS_ERR(reset_gpio))558		return PTR_ERR(reset_gpio);559	if (reset_gpio) {560		/* datasheet: need > 10us for a digital + analog reset */561		usleep_range(15, 50);562		/* take the chip out of reset */563		gpiod_set_value_cansleep(reset_gpio, 0);564		/* datasheet: need > 3ms for digital section to become stable */565		usleep_range(3000, 10000);566	}567 568	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);569	if (!priv)570		return -ENOMEM;571 572	priv->dev = dev;573	dev_set_drvdata(dev, priv);574	priv->regmap = devm_regmap_init(dev, &zl38_regmap_bus, spi,575					&zl38_regmap_conf);576	if (IS_ERR(priv->regmap))577		return PTR_ERR(priv->regmap);578 579	err = zl38_load_firmware(dev, priv->regmap);580	if (err)581		return err;582 583	err = zl38_check_revision(dev, priv->regmap);584	if (err)585		return err;586 587	priv->gpio_chip = devm_kmemdup(dev, &template_chip,588				       sizeof(template_chip), GFP_KERNEL);589	if (!priv->gpio_chip)590		return -ENOMEM;591	priv->gpio_chip->parent = dev;592	err = devm_gpiochip_add_data(dev, priv->gpio_chip, priv->regmap);593	if (err)594		return err;595 596	/* setup the cross-point switch for stereo bypass */597	err = regmap_multi_reg_write(priv->regmap, cp_config_stereo_bypass,598				     ARRAY_SIZE(cp_config_stereo_bypass));599	if (err)600		return err;601	/* setup for 12MHz crystal connected to the chip */602	err = regmap_update_bits(priv->regmap, REG_CLK_CFG, CLK_CFG_SOURCE_XTAL,603				 CLK_CFG_SOURCE_XTAL);604	if (err)605		return err;606 607	return devm_snd_soc_register_component(dev, &zl38_component_dev,608					       &zl38_dai, 1);609}610 611static const struct of_device_id zl38_dt_ids[] __maybe_unused = {612	{ .compatible = "mscc,zl38060", },613	{ /* sentinel */ }614};615MODULE_DEVICE_TABLE(of, zl38_dt_ids);616 617static const struct spi_device_id zl38_spi_ids[] = {618	{ "zl38060", 0 },619	{ /* sentinel */ }620};621MODULE_DEVICE_TABLE(spi, zl38_spi_ids);622 623static struct spi_driver zl38060_spi_driver = {624	.driver	= {625		.name = DRV_NAME,626		.of_match_table = of_match_ptr(zl38_dt_ids),627	},628	.probe = zl38_spi_probe,629	.id_table = zl38_spi_ids,630};631module_spi_driver(zl38060_spi_driver);632 633MODULE_DESCRIPTION("ASoC ZL38060 driver");634MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");635MODULE_LICENSE("GPL v2");636