162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * wm0010.c -- WM0010 DSP Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2012 Wolfson Microelectronics PLC. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Authors: Mark Brown <broonie@opensource.wolfsonmicro.com> 862306a36Sopenharmony_ci * Dimitris Papastamos <dp@opensource.wolfsonmicro.com> 962306a36Sopenharmony_ci * Scott Ling <sl@opensource.wolfsonmicro.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/moduleparam.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/irqreturn.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/spi/spi.h> 1862306a36Sopenharmony_ci#include <linux/firmware.h> 1962306a36Sopenharmony_ci#include <linux/delay.h> 2062306a36Sopenharmony_ci#include <linux/fs.h> 2162306a36Sopenharmony_ci#include <linux/gpio.h> 2262306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2362306a36Sopenharmony_ci#include <linux/mutex.h> 2462306a36Sopenharmony_ci#include <linux/workqueue.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <sound/soc.h> 2762306a36Sopenharmony_ci#include <sound/wm0010.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define DEVICE_ID_WM0010 10 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* We only support v1 of the .dfw INFO record */ 3262306a36Sopenharmony_ci#define INFO_VERSION 1 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cienum dfw_cmd { 3562306a36Sopenharmony_ci DFW_CMD_FUSE = 0x01, 3662306a36Sopenharmony_ci DFW_CMD_CODE_HDR, 3762306a36Sopenharmony_ci DFW_CMD_CODE_DATA, 3862306a36Sopenharmony_ci DFW_CMD_PLL, 3962306a36Sopenharmony_ci DFW_CMD_INFO = 0xff 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistruct dfw_binrec { 4362306a36Sopenharmony_ci u8 command; 4462306a36Sopenharmony_ci u32 length:24; 4562306a36Sopenharmony_ci u32 address; 4662306a36Sopenharmony_ci uint8_t data[]; 4762306a36Sopenharmony_ci} __packed; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct dfw_inforec { 5062306a36Sopenharmony_ci u8 info_version; 5162306a36Sopenharmony_ci u8 tool_major_version; 5262306a36Sopenharmony_ci u8 tool_minor_version; 5362306a36Sopenharmony_ci u8 dsp_target; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistruct dfw_pllrec { 5762306a36Sopenharmony_ci u8 command; 5862306a36Sopenharmony_ci u32 length:24; 5962306a36Sopenharmony_ci u32 address; 6062306a36Sopenharmony_ci u32 clkctrl1; 6162306a36Sopenharmony_ci u32 clkctrl2; 6262306a36Sopenharmony_ci u32 clkctrl3; 6362306a36Sopenharmony_ci u32 ldetctrl; 6462306a36Sopenharmony_ci u32 uart_div; 6562306a36Sopenharmony_ci u32 spi_div; 6662306a36Sopenharmony_ci} __packed; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic struct pll_clock_map { 6962306a36Sopenharmony_ci int max_sysclk; 7062306a36Sopenharmony_ci int max_pll_spi_speed; 7162306a36Sopenharmony_ci u32 pll_clkctrl1; 7262306a36Sopenharmony_ci} pll_clock_map[] = { /* Dividers */ 7362306a36Sopenharmony_ci { 22000000, 26000000, 0x00201f11 }, /* 2,32,2 */ 7462306a36Sopenharmony_ci { 18000000, 26000000, 0x00203f21 }, /* 2,64,4 */ 7562306a36Sopenharmony_ci { 14000000, 26000000, 0x00202620 }, /* 1,39,4 */ 7662306a36Sopenharmony_ci { 10000000, 22000000, 0x00203120 }, /* 1,50,4 */ 7762306a36Sopenharmony_ci { 6500000, 22000000, 0x00204520 }, /* 1,70,4 */ 7862306a36Sopenharmony_ci { 5500000, 22000000, 0x00103f10 }, /* 1,64,2 */ 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cienum wm0010_state { 8262306a36Sopenharmony_ci WM0010_POWER_OFF, 8362306a36Sopenharmony_ci WM0010_OUT_OF_RESET, 8462306a36Sopenharmony_ci WM0010_BOOTROM, 8562306a36Sopenharmony_ci WM0010_STAGE2, 8662306a36Sopenharmony_ci WM0010_FIRMWARE, 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistruct wm0010_priv { 9062306a36Sopenharmony_ci struct snd_soc_component *component; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci struct mutex lock; 9362306a36Sopenharmony_ci struct device *dev; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci struct wm0010_pdata pdata; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci int gpio_reset; 9862306a36Sopenharmony_ci int gpio_reset_value; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci struct regulator_bulk_data core_supplies[2]; 10162306a36Sopenharmony_ci struct regulator *dbvdd; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci int sysclk; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci enum wm0010_state state; 10662306a36Sopenharmony_ci bool boot_failed; 10762306a36Sopenharmony_ci bool ready; 10862306a36Sopenharmony_ci bool pll_running; 10962306a36Sopenharmony_ci int max_spi_freq; 11062306a36Sopenharmony_ci int board_max_spi_speed; 11162306a36Sopenharmony_ci u32 pll_clkctrl1; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci spinlock_t irq_lock; 11462306a36Sopenharmony_ci int irq; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci struct completion boot_completion; 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistruct wm0010_spi_msg { 12062306a36Sopenharmony_ci struct spi_message m; 12162306a36Sopenharmony_ci struct spi_transfer t; 12262306a36Sopenharmony_ci u8 *tx_buf; 12362306a36Sopenharmony_ci u8 *rx_buf; 12462306a36Sopenharmony_ci size_t len; 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget wm0010_dapm_widgets[] = { 12862306a36Sopenharmony_ciSND_SOC_DAPM_SUPPLY("CLKIN", SND_SOC_NOPM, 0, 0, NULL, 0), 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic const struct snd_soc_dapm_route wm0010_dapm_routes[] = { 13262306a36Sopenharmony_ci { "SDI2 Capture", NULL, "SDI1 Playback" }, 13362306a36Sopenharmony_ci { "SDI1 Capture", NULL, "SDI2 Playback" }, 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci { "SDI1 Capture", NULL, "CLKIN" }, 13662306a36Sopenharmony_ci { "SDI2 Capture", NULL, "CLKIN" }, 13762306a36Sopenharmony_ci { "SDI1 Playback", NULL, "CLKIN" }, 13862306a36Sopenharmony_ci { "SDI2 Playback", NULL, "CLKIN" }, 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic const char *wm0010_state_to_str(enum wm0010_state state) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci static const char * const state_to_str[] = { 14462306a36Sopenharmony_ci "Power off", 14562306a36Sopenharmony_ci "Out of reset", 14662306a36Sopenharmony_ci "Boot ROM", 14762306a36Sopenharmony_ci "Stage2", 14862306a36Sopenharmony_ci "Firmware" 14962306a36Sopenharmony_ci }; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (state < 0 || state >= ARRAY_SIZE(state_to_str)) 15262306a36Sopenharmony_ci return "null"; 15362306a36Sopenharmony_ci return state_to_str[state]; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* Called with wm0010->lock held */ 15762306a36Sopenharmony_cistatic void wm0010_halt(struct snd_soc_component *component) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); 16062306a36Sopenharmony_ci unsigned long flags; 16162306a36Sopenharmony_ci enum wm0010_state state; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* Fetch the wm0010 state */ 16462306a36Sopenharmony_ci spin_lock_irqsave(&wm0010->irq_lock, flags); 16562306a36Sopenharmony_ci state = wm0010->state; 16662306a36Sopenharmony_ci spin_unlock_irqrestore(&wm0010->irq_lock, flags); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci switch (state) { 16962306a36Sopenharmony_ci case WM0010_POWER_OFF: 17062306a36Sopenharmony_ci /* If there's nothing to do, bail out */ 17162306a36Sopenharmony_ci return; 17262306a36Sopenharmony_ci case WM0010_OUT_OF_RESET: 17362306a36Sopenharmony_ci case WM0010_BOOTROM: 17462306a36Sopenharmony_ci case WM0010_STAGE2: 17562306a36Sopenharmony_ci case WM0010_FIRMWARE: 17662306a36Sopenharmony_ci /* Remember to put chip back into reset */ 17762306a36Sopenharmony_ci gpio_set_value_cansleep(wm0010->gpio_reset, 17862306a36Sopenharmony_ci wm0010->gpio_reset_value); 17962306a36Sopenharmony_ci /* Disable the regulators */ 18062306a36Sopenharmony_ci regulator_disable(wm0010->dbvdd); 18162306a36Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(wm0010->core_supplies), 18262306a36Sopenharmony_ci wm0010->core_supplies); 18362306a36Sopenharmony_ci break; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci spin_lock_irqsave(&wm0010->irq_lock, flags); 18762306a36Sopenharmony_ci wm0010->state = WM0010_POWER_OFF; 18862306a36Sopenharmony_ci spin_unlock_irqrestore(&wm0010->irq_lock, flags); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistruct wm0010_boot_xfer { 19262306a36Sopenharmony_ci struct list_head list; 19362306a36Sopenharmony_ci struct snd_soc_component *component; 19462306a36Sopenharmony_ci struct completion *done; 19562306a36Sopenharmony_ci struct spi_message m; 19662306a36Sopenharmony_ci struct spi_transfer t; 19762306a36Sopenharmony_ci}; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/* Called with wm0010->lock held */ 20062306a36Sopenharmony_cistatic void wm0010_mark_boot_failure(struct wm0010_priv *wm0010) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci enum wm0010_state state; 20362306a36Sopenharmony_ci unsigned long flags; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci spin_lock_irqsave(&wm0010->irq_lock, flags); 20662306a36Sopenharmony_ci state = wm0010->state; 20762306a36Sopenharmony_ci spin_unlock_irqrestore(&wm0010->irq_lock, flags); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci dev_err(wm0010->dev, "Failed to transition from `%s' state to `%s' state\n", 21062306a36Sopenharmony_ci wm0010_state_to_str(state), wm0010_state_to_str(state + 1)); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci wm0010->boot_failed = true; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic void wm0010_boot_xfer_complete(void *data) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct wm0010_boot_xfer *xfer = data; 21862306a36Sopenharmony_ci struct snd_soc_component *component = xfer->component; 21962306a36Sopenharmony_ci struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); 22062306a36Sopenharmony_ci u32 *out32 = xfer->t.rx_buf; 22162306a36Sopenharmony_ci int i; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (xfer->m.status != 0) { 22462306a36Sopenharmony_ci dev_err(component->dev, "SPI transfer failed: %d\n", 22562306a36Sopenharmony_ci xfer->m.status); 22662306a36Sopenharmony_ci wm0010_mark_boot_failure(wm0010); 22762306a36Sopenharmony_ci if (xfer->done) 22862306a36Sopenharmony_ci complete(xfer->done); 22962306a36Sopenharmony_ci return; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci for (i = 0; i < xfer->t.len / 4; i++) { 23362306a36Sopenharmony_ci dev_dbg(component->dev, "%d: %04x\n", i, out32[i]); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci switch (be32_to_cpu(out32[i])) { 23662306a36Sopenharmony_ci case 0xe0e0e0e0: 23762306a36Sopenharmony_ci dev_err(component->dev, 23862306a36Sopenharmony_ci "%d: ROM error reported in stage 2\n", i); 23962306a36Sopenharmony_ci wm0010_mark_boot_failure(wm0010); 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci case 0x55555555: 24362306a36Sopenharmony_ci if (wm0010->state < WM0010_STAGE2) 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci dev_err(component->dev, 24662306a36Sopenharmony_ci "%d: ROM bootloader running in stage 2\n", i); 24762306a36Sopenharmony_ci wm0010_mark_boot_failure(wm0010); 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci case 0x0fed0000: 25162306a36Sopenharmony_ci dev_dbg(component->dev, "Stage2 loader running\n"); 25262306a36Sopenharmony_ci break; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci case 0x0fed0007: 25562306a36Sopenharmony_ci dev_dbg(component->dev, "CODE_HDR packet received\n"); 25662306a36Sopenharmony_ci break; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci case 0x0fed0008: 25962306a36Sopenharmony_ci dev_dbg(component->dev, "CODE_DATA packet received\n"); 26062306a36Sopenharmony_ci break; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci case 0x0fed0009: 26362306a36Sopenharmony_ci dev_dbg(component->dev, "Download complete\n"); 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci case 0x0fed000c: 26762306a36Sopenharmony_ci dev_dbg(component->dev, "Application start\n"); 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci case 0x0fed000e: 27162306a36Sopenharmony_ci dev_dbg(component->dev, "PLL packet received\n"); 27262306a36Sopenharmony_ci wm0010->pll_running = true; 27362306a36Sopenharmony_ci break; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci case 0x0fed0025: 27662306a36Sopenharmony_ci dev_err(component->dev, "Device reports image too long\n"); 27762306a36Sopenharmony_ci wm0010_mark_boot_failure(wm0010); 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci case 0x0fed002c: 28162306a36Sopenharmony_ci dev_err(component->dev, "Device reports bad SPI packet\n"); 28262306a36Sopenharmony_ci wm0010_mark_boot_failure(wm0010); 28362306a36Sopenharmony_ci break; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci case 0x0fed0031: 28662306a36Sopenharmony_ci dev_err(component->dev, "Device reports SPI read overflow\n"); 28762306a36Sopenharmony_ci wm0010_mark_boot_failure(wm0010); 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci case 0x0fed0032: 29162306a36Sopenharmony_ci dev_err(component->dev, "Device reports SPI underclock\n"); 29262306a36Sopenharmony_ci wm0010_mark_boot_failure(wm0010); 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci case 0x0fed0033: 29662306a36Sopenharmony_ci dev_err(component->dev, "Device reports bad header packet\n"); 29762306a36Sopenharmony_ci wm0010_mark_boot_failure(wm0010); 29862306a36Sopenharmony_ci break; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci case 0x0fed0034: 30162306a36Sopenharmony_ci dev_err(component->dev, "Device reports invalid packet type\n"); 30262306a36Sopenharmony_ci wm0010_mark_boot_failure(wm0010); 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci case 0x0fed0035: 30662306a36Sopenharmony_ci dev_err(component->dev, "Device reports data before header error\n"); 30762306a36Sopenharmony_ci wm0010_mark_boot_failure(wm0010); 30862306a36Sopenharmony_ci break; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci case 0x0fed0038: 31162306a36Sopenharmony_ci dev_err(component->dev, "Device reports invalid PLL packet\n"); 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci case 0x0fed003a: 31562306a36Sopenharmony_ci dev_err(component->dev, "Device reports packet alignment error\n"); 31662306a36Sopenharmony_ci wm0010_mark_boot_failure(wm0010); 31762306a36Sopenharmony_ci break; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci default: 32062306a36Sopenharmony_ci dev_err(component->dev, "Unrecognised return 0x%x\n", 32162306a36Sopenharmony_ci be32_to_cpu(out32[i])); 32262306a36Sopenharmony_ci wm0010_mark_boot_failure(wm0010); 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (wm0010->boot_failed) 32762306a36Sopenharmony_ci break; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (xfer->done) 33162306a36Sopenharmony_ci complete(xfer->done); 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic void byte_swap_64(u64 *data_in, u64 *data_out, u32 len) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci int i; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci for (i = 0; i < len / 8; i++) 33962306a36Sopenharmony_ci data_out[i] = cpu_to_be64(le64_to_cpu(data_in[i])); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic int wm0010_firmware_load(const char *name, struct snd_soc_component *component) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci struct spi_device *spi = to_spi_device(component->dev); 34562306a36Sopenharmony_ci struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); 34662306a36Sopenharmony_ci struct list_head xfer_list; 34762306a36Sopenharmony_ci struct wm0010_boot_xfer *xfer; 34862306a36Sopenharmony_ci int ret; 34962306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(done); 35062306a36Sopenharmony_ci const struct firmware *fw; 35162306a36Sopenharmony_ci const struct dfw_binrec *rec; 35262306a36Sopenharmony_ci const struct dfw_inforec *inforec; 35362306a36Sopenharmony_ci u64 *img; 35462306a36Sopenharmony_ci u8 *out, dsp; 35562306a36Sopenharmony_ci u32 len, offset; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci INIT_LIST_HEAD(&xfer_list); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci ret = request_firmware(&fw, name, component->dev); 36062306a36Sopenharmony_ci if (ret != 0) { 36162306a36Sopenharmony_ci dev_err(component->dev, "Failed to request application(%s): %d\n", 36262306a36Sopenharmony_ci name, ret); 36362306a36Sopenharmony_ci return ret; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci rec = (const struct dfw_binrec *)fw->data; 36762306a36Sopenharmony_ci inforec = (const struct dfw_inforec *)rec->data; 36862306a36Sopenharmony_ci offset = 0; 36962306a36Sopenharmony_ci dsp = inforec->dsp_target; 37062306a36Sopenharmony_ci wm0010->boot_failed = false; 37162306a36Sopenharmony_ci if (WARN_ON(!list_empty(&xfer_list))) 37262306a36Sopenharmony_ci return -EINVAL; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* First record should be INFO */ 37562306a36Sopenharmony_ci if (rec->command != DFW_CMD_INFO) { 37662306a36Sopenharmony_ci dev_err(component->dev, "First record not INFO\r\n"); 37762306a36Sopenharmony_ci ret = -EINVAL; 37862306a36Sopenharmony_ci goto abort; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (inforec->info_version != INFO_VERSION) { 38262306a36Sopenharmony_ci dev_err(component->dev, 38362306a36Sopenharmony_ci "Unsupported version (%02d) of INFO record\r\n", 38462306a36Sopenharmony_ci inforec->info_version); 38562306a36Sopenharmony_ci ret = -EINVAL; 38662306a36Sopenharmony_ci goto abort; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci dev_dbg(component->dev, "Version v%02d INFO record found\r\n", 39062306a36Sopenharmony_ci inforec->info_version); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* Check it's a DSP file */ 39362306a36Sopenharmony_ci if (dsp != DEVICE_ID_WM0010) { 39462306a36Sopenharmony_ci dev_err(component->dev, "Not a WM0010 firmware file.\r\n"); 39562306a36Sopenharmony_ci ret = -EINVAL; 39662306a36Sopenharmony_ci goto abort; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* Skip the info record as we don't need to send it */ 40062306a36Sopenharmony_ci offset += ((rec->length) + 8); 40162306a36Sopenharmony_ci rec = (void *)&rec->data[rec->length]; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci while (offset < fw->size) { 40462306a36Sopenharmony_ci dev_dbg(component->dev, 40562306a36Sopenharmony_ci "Packet: command %d, data length = 0x%x\r\n", 40662306a36Sopenharmony_ci rec->command, rec->length); 40762306a36Sopenharmony_ci len = rec->length + 8; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci xfer = kzalloc(sizeof(*xfer), GFP_KERNEL); 41062306a36Sopenharmony_ci if (!xfer) { 41162306a36Sopenharmony_ci ret = -ENOMEM; 41262306a36Sopenharmony_ci goto abort; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci xfer->component = component; 41662306a36Sopenharmony_ci list_add_tail(&xfer->list, &xfer_list); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci out = kzalloc(len, GFP_KERNEL | GFP_DMA); 41962306a36Sopenharmony_ci if (!out) { 42062306a36Sopenharmony_ci ret = -ENOMEM; 42162306a36Sopenharmony_ci goto abort1; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci xfer->t.rx_buf = out; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci img = kzalloc(len, GFP_KERNEL | GFP_DMA); 42662306a36Sopenharmony_ci if (!img) { 42762306a36Sopenharmony_ci ret = -ENOMEM; 42862306a36Sopenharmony_ci goto abort1; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci xfer->t.tx_buf = img; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci byte_swap_64((u64 *)&rec->command, img, len); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci spi_message_init(&xfer->m); 43562306a36Sopenharmony_ci xfer->m.complete = wm0010_boot_xfer_complete; 43662306a36Sopenharmony_ci xfer->m.context = xfer; 43762306a36Sopenharmony_ci xfer->t.len = len; 43862306a36Sopenharmony_ci xfer->t.bits_per_word = 8; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (!wm0010->pll_running) { 44162306a36Sopenharmony_ci xfer->t.speed_hz = wm0010->sysclk / 6; 44262306a36Sopenharmony_ci } else { 44362306a36Sopenharmony_ci xfer->t.speed_hz = wm0010->max_spi_freq; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (wm0010->board_max_spi_speed && 44662306a36Sopenharmony_ci (wm0010->board_max_spi_speed < wm0010->max_spi_freq)) 44762306a36Sopenharmony_ci xfer->t.speed_hz = wm0010->board_max_spi_speed; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* Store max usable spi frequency for later use */ 45162306a36Sopenharmony_ci wm0010->max_spi_freq = xfer->t.speed_hz; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci spi_message_add_tail(&xfer->t, &xfer->m); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci offset += ((rec->length) + 8); 45662306a36Sopenharmony_ci rec = (void *)&rec->data[rec->length]; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (offset >= fw->size) { 45962306a36Sopenharmony_ci dev_dbg(component->dev, "All transfers scheduled\n"); 46062306a36Sopenharmony_ci xfer->done = &done; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci ret = spi_async(spi, &xfer->m); 46462306a36Sopenharmony_ci if (ret != 0) { 46562306a36Sopenharmony_ci dev_err(component->dev, "Write failed: %d\n", ret); 46662306a36Sopenharmony_ci goto abort1; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (wm0010->boot_failed) { 47062306a36Sopenharmony_ci dev_dbg(component->dev, "Boot fail!\n"); 47162306a36Sopenharmony_ci ret = -EINVAL; 47262306a36Sopenharmony_ci goto abort1; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci wait_for_completion(&done); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci ret = 0; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ciabort1: 48162306a36Sopenharmony_ci while (!list_empty(&xfer_list)) { 48262306a36Sopenharmony_ci xfer = list_first_entry(&xfer_list, struct wm0010_boot_xfer, 48362306a36Sopenharmony_ci list); 48462306a36Sopenharmony_ci kfree(xfer->t.rx_buf); 48562306a36Sopenharmony_ci kfree(xfer->t.tx_buf); 48662306a36Sopenharmony_ci list_del(&xfer->list); 48762306a36Sopenharmony_ci kfree(xfer); 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ciabort: 49162306a36Sopenharmony_ci release_firmware(fw); 49262306a36Sopenharmony_ci return ret; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic int wm0010_stage2_load(struct snd_soc_component *component) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct spi_device *spi = to_spi_device(component->dev); 49862306a36Sopenharmony_ci struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); 49962306a36Sopenharmony_ci const struct firmware *fw; 50062306a36Sopenharmony_ci struct spi_message m; 50162306a36Sopenharmony_ci struct spi_transfer t; 50262306a36Sopenharmony_ci u32 *img; 50362306a36Sopenharmony_ci u8 *out; 50462306a36Sopenharmony_ci int i; 50562306a36Sopenharmony_ci int ret = 0; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci ret = request_firmware(&fw, "wm0010_stage2.bin", component->dev); 50862306a36Sopenharmony_ci if (ret != 0) { 50962306a36Sopenharmony_ci dev_err(component->dev, "Failed to request stage2 loader: %d\n", 51062306a36Sopenharmony_ci ret); 51162306a36Sopenharmony_ci return ret; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci dev_dbg(component->dev, "Downloading %zu byte stage 2 loader\n", fw->size); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* Copy to local buffer first as vmalloc causes problems for dma */ 51762306a36Sopenharmony_ci img = kmemdup(&fw->data[0], fw->size, GFP_KERNEL | GFP_DMA); 51862306a36Sopenharmony_ci if (!img) { 51962306a36Sopenharmony_ci ret = -ENOMEM; 52062306a36Sopenharmony_ci goto abort2; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci out = kzalloc(fw->size, GFP_KERNEL | GFP_DMA); 52462306a36Sopenharmony_ci if (!out) { 52562306a36Sopenharmony_ci ret = -ENOMEM; 52662306a36Sopenharmony_ci goto abort1; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci spi_message_init(&m); 53062306a36Sopenharmony_ci memset(&t, 0, sizeof(t)); 53162306a36Sopenharmony_ci t.rx_buf = out; 53262306a36Sopenharmony_ci t.tx_buf = img; 53362306a36Sopenharmony_ci t.len = fw->size; 53462306a36Sopenharmony_ci t.bits_per_word = 8; 53562306a36Sopenharmony_ci t.speed_hz = wm0010->sysclk / 10; 53662306a36Sopenharmony_ci spi_message_add_tail(&t, &m); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci dev_dbg(component->dev, "Starting initial download at %dHz\n", 53962306a36Sopenharmony_ci t.speed_hz); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci ret = spi_sync(spi, &m); 54262306a36Sopenharmony_ci if (ret != 0) { 54362306a36Sopenharmony_ci dev_err(component->dev, "Initial download failed: %d\n", ret); 54462306a36Sopenharmony_ci goto abort; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* Look for errors from the boot ROM */ 54862306a36Sopenharmony_ci for (i = 0; i < fw->size; i++) { 54962306a36Sopenharmony_ci if (out[i] != 0x55) { 55062306a36Sopenharmony_ci dev_err(component->dev, "Boot ROM error: %x in %d\n", 55162306a36Sopenharmony_ci out[i], i); 55262306a36Sopenharmony_ci wm0010_mark_boot_failure(wm0010); 55362306a36Sopenharmony_ci ret = -EBUSY; 55462306a36Sopenharmony_ci goto abort; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ciabort: 55862306a36Sopenharmony_ci kfree(out); 55962306a36Sopenharmony_ciabort1: 56062306a36Sopenharmony_ci kfree(img); 56162306a36Sopenharmony_ciabort2: 56262306a36Sopenharmony_ci release_firmware(fw); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return ret; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int wm0010_boot(struct snd_soc_component *component) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct spi_device *spi = to_spi_device(component->dev); 57062306a36Sopenharmony_ci struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); 57162306a36Sopenharmony_ci unsigned long flags; 57262306a36Sopenharmony_ci int ret; 57362306a36Sopenharmony_ci struct spi_message m; 57462306a36Sopenharmony_ci struct spi_transfer t; 57562306a36Sopenharmony_ci struct dfw_pllrec pll_rec; 57662306a36Sopenharmony_ci u32 *p, len; 57762306a36Sopenharmony_ci u64 *img_swap; 57862306a36Sopenharmony_ci u8 *out; 57962306a36Sopenharmony_ci int i; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci spin_lock_irqsave(&wm0010->irq_lock, flags); 58262306a36Sopenharmony_ci if (wm0010->state != WM0010_POWER_OFF) 58362306a36Sopenharmony_ci dev_warn(wm0010->dev, "DSP already powered up!\n"); 58462306a36Sopenharmony_ci spin_unlock_irqrestore(&wm0010->irq_lock, flags); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (wm0010->sysclk > 26000000) { 58762306a36Sopenharmony_ci dev_err(component->dev, "Max DSP clock frequency is 26MHz\n"); 58862306a36Sopenharmony_ci ret = -ECANCELED; 58962306a36Sopenharmony_ci goto err; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci mutex_lock(&wm0010->lock); 59362306a36Sopenharmony_ci wm0010->pll_running = false; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci dev_dbg(component->dev, "max_spi_freq: %d\n", wm0010->max_spi_freq); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(wm0010->core_supplies), 59862306a36Sopenharmony_ci wm0010->core_supplies); 59962306a36Sopenharmony_ci if (ret != 0) { 60062306a36Sopenharmony_ci dev_err(&spi->dev, "Failed to enable core supplies: %d\n", 60162306a36Sopenharmony_ci ret); 60262306a36Sopenharmony_ci mutex_unlock(&wm0010->lock); 60362306a36Sopenharmony_ci goto err; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci ret = regulator_enable(wm0010->dbvdd); 60762306a36Sopenharmony_ci if (ret != 0) { 60862306a36Sopenharmony_ci dev_err(&spi->dev, "Failed to enable DBVDD: %d\n", ret); 60962306a36Sopenharmony_ci goto err_core; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* Release reset */ 61362306a36Sopenharmony_ci gpio_set_value_cansleep(wm0010->gpio_reset, !wm0010->gpio_reset_value); 61462306a36Sopenharmony_ci spin_lock_irqsave(&wm0010->irq_lock, flags); 61562306a36Sopenharmony_ci wm0010->state = WM0010_OUT_OF_RESET; 61662306a36Sopenharmony_ci spin_unlock_irqrestore(&wm0010->irq_lock, flags); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (!wait_for_completion_timeout(&wm0010->boot_completion, 61962306a36Sopenharmony_ci msecs_to_jiffies(20))) 62062306a36Sopenharmony_ci dev_err(component->dev, "Failed to get interrupt from DSP\n"); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci spin_lock_irqsave(&wm0010->irq_lock, flags); 62362306a36Sopenharmony_ci wm0010->state = WM0010_BOOTROM; 62462306a36Sopenharmony_ci spin_unlock_irqrestore(&wm0010->irq_lock, flags); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci ret = wm0010_stage2_load(component); 62762306a36Sopenharmony_ci if (ret) 62862306a36Sopenharmony_ci goto abort; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (!wait_for_completion_timeout(&wm0010->boot_completion, 63162306a36Sopenharmony_ci msecs_to_jiffies(20))) 63262306a36Sopenharmony_ci dev_err(component->dev, "Failed to get interrupt from DSP loader.\n"); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci spin_lock_irqsave(&wm0010->irq_lock, flags); 63562306a36Sopenharmony_ci wm0010->state = WM0010_STAGE2; 63662306a36Sopenharmony_ci spin_unlock_irqrestore(&wm0010->irq_lock, flags); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* Only initialise PLL if max_spi_freq initialised */ 63962306a36Sopenharmony_ci if (wm0010->max_spi_freq) { 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* Initialise a PLL record */ 64262306a36Sopenharmony_ci memset(&pll_rec, 0, sizeof(pll_rec)); 64362306a36Sopenharmony_ci pll_rec.command = DFW_CMD_PLL; 64462306a36Sopenharmony_ci pll_rec.length = (sizeof(pll_rec) - 8); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* On wm0010 only the CLKCTRL1 value is used */ 64762306a36Sopenharmony_ci pll_rec.clkctrl1 = wm0010->pll_clkctrl1; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci ret = -ENOMEM; 65062306a36Sopenharmony_ci len = pll_rec.length + 8; 65162306a36Sopenharmony_ci out = kzalloc(len, GFP_KERNEL | GFP_DMA); 65262306a36Sopenharmony_ci if (!out) 65362306a36Sopenharmony_ci goto abort; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci img_swap = kzalloc(len, GFP_KERNEL | GFP_DMA); 65662306a36Sopenharmony_ci if (!img_swap) 65762306a36Sopenharmony_ci goto abort_out; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* We need to re-order for 0010 */ 66062306a36Sopenharmony_ci byte_swap_64((u64 *)&pll_rec, img_swap, len); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci spi_message_init(&m); 66362306a36Sopenharmony_ci memset(&t, 0, sizeof(t)); 66462306a36Sopenharmony_ci t.rx_buf = out; 66562306a36Sopenharmony_ci t.tx_buf = img_swap; 66662306a36Sopenharmony_ci t.len = len; 66762306a36Sopenharmony_ci t.bits_per_word = 8; 66862306a36Sopenharmony_ci t.speed_hz = wm0010->sysclk / 6; 66962306a36Sopenharmony_ci spi_message_add_tail(&t, &m); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci ret = spi_sync(spi, &m); 67262306a36Sopenharmony_ci if (ret) { 67362306a36Sopenharmony_ci dev_err(component->dev, "First PLL write failed: %d\n", ret); 67462306a36Sopenharmony_ci goto abort_swap; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci /* Use a second send of the message to get the return status */ 67862306a36Sopenharmony_ci ret = spi_sync(spi, &m); 67962306a36Sopenharmony_ci if (ret) { 68062306a36Sopenharmony_ci dev_err(component->dev, "Second PLL write failed: %d\n", ret); 68162306a36Sopenharmony_ci goto abort_swap; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci p = (u32 *)out; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* Look for PLL active code from the DSP */ 68762306a36Sopenharmony_ci for (i = 0; i < len / 4; i++) { 68862306a36Sopenharmony_ci if (*p == 0x0e00ed0f) { 68962306a36Sopenharmony_ci dev_dbg(component->dev, "PLL packet received\n"); 69062306a36Sopenharmony_ci wm0010->pll_running = true; 69162306a36Sopenharmony_ci break; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci p++; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci kfree(img_swap); 69762306a36Sopenharmony_ci kfree(out); 69862306a36Sopenharmony_ci } else 69962306a36Sopenharmony_ci dev_dbg(component->dev, "Not enabling DSP PLL."); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci ret = wm0010_firmware_load("wm0010.dfw", component); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (ret != 0) 70462306a36Sopenharmony_ci goto abort; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci spin_lock_irqsave(&wm0010->irq_lock, flags); 70762306a36Sopenharmony_ci wm0010->state = WM0010_FIRMWARE; 70862306a36Sopenharmony_ci spin_unlock_irqrestore(&wm0010->irq_lock, flags); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci mutex_unlock(&wm0010->lock); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci return 0; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ciabort_swap: 71562306a36Sopenharmony_ci kfree(img_swap); 71662306a36Sopenharmony_ciabort_out: 71762306a36Sopenharmony_ci kfree(out); 71862306a36Sopenharmony_ciabort: 71962306a36Sopenharmony_ci /* Put the chip back into reset */ 72062306a36Sopenharmony_ci wm0010_halt(component); 72162306a36Sopenharmony_ci mutex_unlock(&wm0010->lock); 72262306a36Sopenharmony_ci return ret; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cierr_core: 72562306a36Sopenharmony_ci mutex_unlock(&wm0010->lock); 72662306a36Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(wm0010->core_supplies), 72762306a36Sopenharmony_ci wm0010->core_supplies); 72862306a36Sopenharmony_cierr: 72962306a36Sopenharmony_ci return ret; 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic int wm0010_set_bias_level(struct snd_soc_component *component, 73362306a36Sopenharmony_ci enum snd_soc_bias_level level) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci switch (level) { 73862306a36Sopenharmony_ci case SND_SOC_BIAS_ON: 73962306a36Sopenharmony_ci if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_PREPARE) 74062306a36Sopenharmony_ci wm0010_boot(component); 74162306a36Sopenharmony_ci break; 74262306a36Sopenharmony_ci case SND_SOC_BIAS_PREPARE: 74362306a36Sopenharmony_ci break; 74462306a36Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 74562306a36Sopenharmony_ci if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_PREPARE) { 74662306a36Sopenharmony_ci mutex_lock(&wm0010->lock); 74762306a36Sopenharmony_ci wm0010_halt(component); 74862306a36Sopenharmony_ci mutex_unlock(&wm0010->lock); 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci break; 75162306a36Sopenharmony_ci case SND_SOC_BIAS_OFF: 75262306a36Sopenharmony_ci break; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci return 0; 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic int wm0010_set_sysclk(struct snd_soc_component *component, int source, 75962306a36Sopenharmony_ci int clk_id, unsigned int freq, int dir) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); 76262306a36Sopenharmony_ci unsigned int i; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci wm0010->sysclk = freq; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (freq < pll_clock_map[ARRAY_SIZE(pll_clock_map)-1].max_sysclk) { 76762306a36Sopenharmony_ci wm0010->max_spi_freq = 0; 76862306a36Sopenharmony_ci } else { 76962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pll_clock_map); i++) 77062306a36Sopenharmony_ci if (freq >= pll_clock_map[i].max_sysclk) { 77162306a36Sopenharmony_ci wm0010->max_spi_freq = pll_clock_map[i].max_pll_spi_speed; 77262306a36Sopenharmony_ci wm0010->pll_clkctrl1 = pll_clock_map[i].pll_clkctrl1; 77362306a36Sopenharmony_ci break; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci return 0; 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic int wm0010_probe(struct snd_soc_component *component); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_wm0010 = { 78362306a36Sopenharmony_ci .probe = wm0010_probe, 78462306a36Sopenharmony_ci .set_bias_level = wm0010_set_bias_level, 78562306a36Sopenharmony_ci .set_sysclk = wm0010_set_sysclk, 78662306a36Sopenharmony_ci .dapm_widgets = wm0010_dapm_widgets, 78762306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(wm0010_dapm_widgets), 78862306a36Sopenharmony_ci .dapm_routes = wm0010_dapm_routes, 78962306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(wm0010_dapm_routes), 79062306a36Sopenharmony_ci .use_pmdown_time = 1, 79162306a36Sopenharmony_ci .endianness = 1, 79262306a36Sopenharmony_ci}; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci#define WM0010_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) 79562306a36Sopenharmony_ci#define WM0010_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ 79662306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |\ 79762306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE) 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic struct snd_soc_dai_driver wm0010_dai[] = { 80062306a36Sopenharmony_ci { 80162306a36Sopenharmony_ci .name = "wm0010-sdi1", 80262306a36Sopenharmony_ci .playback = { 80362306a36Sopenharmony_ci .stream_name = "SDI1 Playback", 80462306a36Sopenharmony_ci .channels_min = 1, 80562306a36Sopenharmony_ci .channels_max = 2, 80662306a36Sopenharmony_ci .rates = WM0010_RATES, 80762306a36Sopenharmony_ci .formats = WM0010_FORMATS, 80862306a36Sopenharmony_ci }, 80962306a36Sopenharmony_ci .capture = { 81062306a36Sopenharmony_ci .stream_name = "SDI1 Capture", 81162306a36Sopenharmony_ci .channels_min = 1, 81262306a36Sopenharmony_ci .channels_max = 2, 81362306a36Sopenharmony_ci .rates = WM0010_RATES, 81462306a36Sopenharmony_ci .formats = WM0010_FORMATS, 81562306a36Sopenharmony_ci }, 81662306a36Sopenharmony_ci }, 81762306a36Sopenharmony_ci { 81862306a36Sopenharmony_ci .name = "wm0010-sdi2", 81962306a36Sopenharmony_ci .playback = { 82062306a36Sopenharmony_ci .stream_name = "SDI2 Playback", 82162306a36Sopenharmony_ci .channels_min = 1, 82262306a36Sopenharmony_ci .channels_max = 2, 82362306a36Sopenharmony_ci .rates = WM0010_RATES, 82462306a36Sopenharmony_ci .formats = WM0010_FORMATS, 82562306a36Sopenharmony_ci }, 82662306a36Sopenharmony_ci .capture = { 82762306a36Sopenharmony_ci .stream_name = "SDI2 Capture", 82862306a36Sopenharmony_ci .channels_min = 1, 82962306a36Sopenharmony_ci .channels_max = 2, 83062306a36Sopenharmony_ci .rates = WM0010_RATES, 83162306a36Sopenharmony_ci .formats = WM0010_FORMATS, 83262306a36Sopenharmony_ci }, 83362306a36Sopenharmony_ci }, 83462306a36Sopenharmony_ci}; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic irqreturn_t wm0010_irq(int irq, void *data) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci struct wm0010_priv *wm0010 = data; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci switch (wm0010->state) { 84162306a36Sopenharmony_ci case WM0010_OUT_OF_RESET: 84262306a36Sopenharmony_ci case WM0010_BOOTROM: 84362306a36Sopenharmony_ci case WM0010_STAGE2: 84462306a36Sopenharmony_ci spin_lock(&wm0010->irq_lock); 84562306a36Sopenharmony_ci complete(&wm0010->boot_completion); 84662306a36Sopenharmony_ci spin_unlock(&wm0010->irq_lock); 84762306a36Sopenharmony_ci return IRQ_HANDLED; 84862306a36Sopenharmony_ci default: 84962306a36Sopenharmony_ci return IRQ_NONE; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci return IRQ_NONE; 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_cistatic int wm0010_probe(struct snd_soc_component *component) 85662306a36Sopenharmony_ci{ 85762306a36Sopenharmony_ci struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci wm0010->component = component; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci return 0; 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistatic int wm0010_spi_probe(struct spi_device *spi) 86562306a36Sopenharmony_ci{ 86662306a36Sopenharmony_ci unsigned long gpio_flags; 86762306a36Sopenharmony_ci int ret; 86862306a36Sopenharmony_ci int trigger; 86962306a36Sopenharmony_ci int irq; 87062306a36Sopenharmony_ci struct wm0010_priv *wm0010; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci wm0010 = devm_kzalloc(&spi->dev, sizeof(*wm0010), 87362306a36Sopenharmony_ci GFP_KERNEL); 87462306a36Sopenharmony_ci if (!wm0010) 87562306a36Sopenharmony_ci return -ENOMEM; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci mutex_init(&wm0010->lock); 87862306a36Sopenharmony_ci spin_lock_init(&wm0010->irq_lock); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci spi_set_drvdata(spi, wm0010); 88162306a36Sopenharmony_ci wm0010->dev = &spi->dev; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (dev_get_platdata(&spi->dev)) 88462306a36Sopenharmony_ci memcpy(&wm0010->pdata, dev_get_platdata(&spi->dev), 88562306a36Sopenharmony_ci sizeof(wm0010->pdata)); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci init_completion(&wm0010->boot_completion); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci wm0010->core_supplies[0].supply = "AVDD"; 89062306a36Sopenharmony_ci wm0010->core_supplies[1].supply = "DCVDD"; 89162306a36Sopenharmony_ci ret = devm_regulator_bulk_get(wm0010->dev, ARRAY_SIZE(wm0010->core_supplies), 89262306a36Sopenharmony_ci wm0010->core_supplies); 89362306a36Sopenharmony_ci if (ret != 0) { 89462306a36Sopenharmony_ci dev_err(wm0010->dev, "Failed to obtain core supplies: %d\n", 89562306a36Sopenharmony_ci ret); 89662306a36Sopenharmony_ci return ret; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci wm0010->dbvdd = devm_regulator_get(wm0010->dev, "DBVDD"); 90062306a36Sopenharmony_ci if (IS_ERR(wm0010->dbvdd)) { 90162306a36Sopenharmony_ci ret = PTR_ERR(wm0010->dbvdd); 90262306a36Sopenharmony_ci dev_err(wm0010->dev, "Failed to obtain DBVDD: %d\n", ret); 90362306a36Sopenharmony_ci return ret; 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci if (wm0010->pdata.gpio_reset) { 90762306a36Sopenharmony_ci wm0010->gpio_reset = wm0010->pdata.gpio_reset; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci if (wm0010->pdata.reset_active_high) 91062306a36Sopenharmony_ci wm0010->gpio_reset_value = 1; 91162306a36Sopenharmony_ci else 91262306a36Sopenharmony_ci wm0010->gpio_reset_value = 0; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if (wm0010->gpio_reset_value) 91562306a36Sopenharmony_ci gpio_flags = GPIOF_OUT_INIT_HIGH; 91662306a36Sopenharmony_ci else 91762306a36Sopenharmony_ci gpio_flags = GPIOF_OUT_INIT_LOW; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci ret = devm_gpio_request_one(wm0010->dev, wm0010->gpio_reset, 92062306a36Sopenharmony_ci gpio_flags, "wm0010 reset"); 92162306a36Sopenharmony_ci if (ret < 0) { 92262306a36Sopenharmony_ci dev_err(wm0010->dev, 92362306a36Sopenharmony_ci "Failed to request GPIO for DSP reset: %d\n", 92462306a36Sopenharmony_ci ret); 92562306a36Sopenharmony_ci return ret; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci } else { 92862306a36Sopenharmony_ci dev_err(wm0010->dev, "No reset GPIO configured\n"); 92962306a36Sopenharmony_ci return -EINVAL; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci wm0010->state = WM0010_POWER_OFF; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci irq = spi->irq; 93562306a36Sopenharmony_ci if (wm0010->pdata.irq_flags) 93662306a36Sopenharmony_ci trigger = wm0010->pdata.irq_flags; 93762306a36Sopenharmony_ci else 93862306a36Sopenharmony_ci trigger = IRQF_TRIGGER_FALLING; 93962306a36Sopenharmony_ci trigger |= IRQF_ONESHOT; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci ret = request_threaded_irq(irq, NULL, wm0010_irq, trigger, 94262306a36Sopenharmony_ci "wm0010", wm0010); 94362306a36Sopenharmony_ci if (ret) { 94462306a36Sopenharmony_ci dev_err(wm0010->dev, "Failed to request IRQ %d: %d\n", 94562306a36Sopenharmony_ci irq, ret); 94662306a36Sopenharmony_ci return ret; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci wm0010->irq = irq; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci ret = irq_set_irq_wake(irq, 1); 95162306a36Sopenharmony_ci if (ret) { 95262306a36Sopenharmony_ci dev_err(wm0010->dev, "Failed to set IRQ %d as wake source: %d\n", 95362306a36Sopenharmony_ci irq, ret); 95462306a36Sopenharmony_ci return ret; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci if (spi->max_speed_hz) 95862306a36Sopenharmony_ci wm0010->board_max_spi_speed = spi->max_speed_hz; 95962306a36Sopenharmony_ci else 96062306a36Sopenharmony_ci wm0010->board_max_spi_speed = 0; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci ret = devm_snd_soc_register_component(&spi->dev, 96362306a36Sopenharmony_ci &soc_component_dev_wm0010, wm0010_dai, 96462306a36Sopenharmony_ci ARRAY_SIZE(wm0010_dai)); 96562306a36Sopenharmony_ci if (ret < 0) 96662306a36Sopenharmony_ci return ret; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci return 0; 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic void wm0010_spi_remove(struct spi_device *spi) 97262306a36Sopenharmony_ci{ 97362306a36Sopenharmony_ci struct wm0010_priv *wm0010 = spi_get_drvdata(spi); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci gpio_set_value_cansleep(wm0010->gpio_reset, 97662306a36Sopenharmony_ci wm0010->gpio_reset_value); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci irq_set_irq_wake(wm0010->irq, 0); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci if (wm0010->irq) 98162306a36Sopenharmony_ci free_irq(wm0010->irq, wm0010); 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic struct spi_driver wm0010_spi_driver = { 98562306a36Sopenharmony_ci .driver = { 98662306a36Sopenharmony_ci .name = "wm0010", 98762306a36Sopenharmony_ci }, 98862306a36Sopenharmony_ci .probe = wm0010_spi_probe, 98962306a36Sopenharmony_ci .remove = wm0010_spi_remove, 99062306a36Sopenharmony_ci}; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_cimodule_spi_driver(wm0010_spi_driver); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ciMODULE_DESCRIPTION("ASoC WM0010 driver"); 99562306a36Sopenharmony_ciMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 99662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ciMODULE_FIRMWARE("wm0010.dfw"); 99962306a36Sopenharmony_ciMODULE_FIRMWARE("wm0010_stage2.bin"); 1000