From f9baa0ccb2500be60c51aec6d1f3988dec0df3fe Mon Sep 17 00:00:00 2001 From: Scott Ling Date: Mon, 5 Nov 2012 14:44:25 +0000 Subject: [PATCH 1/6] ASoC: wm0010: Remove boot_done variable as no longer required. Remove the boot_done counter variable and check the wm0010 state variable instead. Signed-off-by: Scott Ling Signed-off-by: Mark Brown --- sound/soc/codecs/wm0010.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 99afc003a084..5c1291748326 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -97,7 +97,6 @@ struct wm0010_priv { enum wm0010_state state; bool boot_failed; - int boot_done; bool ready; bool pll_running; int max_spi_freq; @@ -234,7 +233,7 @@ static void wm0010_boot_xfer_complete(void *data) break; case 0x55555555: - if (wm0010->boot_done == 0) + if (wm0010->state < WM0010_STAGE2) break; dev_err(codec->dev, "%d: ROM bootloader running in stage 2\n", i); @@ -321,7 +320,6 @@ static void wm0010_boot_xfer_complete(void *data) break; } - wm0010->boot_done++; if (xfer->done) complete(xfer->done); } @@ -544,7 +542,6 @@ static int wm0010_boot(struct snd_soc_codec *codec) rec = (const struct dfw_binrec *)fw->data; offset = 0; - wm0010->boot_done = 0; wm0010->boot_failed = false; BUG_ON(!list_empty(&xfer_list)); init_completion(&done); From 8f7d52affeb8341cbc602209b6f35eedb410d3ee Mon Sep 17 00:00:00 2001 From: Scott Ling Date: Wed, 7 Nov 2012 16:53:17 +0000 Subject: [PATCH 2/6] ASoC: wm0010: Split out the firmware file parsing from the boot Move the firmware load and record parsing functionality out into a separate function from the boot function. Signed-off-by: Scott Ling Signed-off-by: Mark Brown --- sound/soc/codecs/wm0010.c | 265 +++++++++++++++++++++----------------- 1 file changed, 147 insertions(+), 118 deletions(-) diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 5c1291748326..7576330044ba 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -332,24 +332,165 @@ static void byte_swap_64(u64 *data_in, u64 *data_out, u32 len) data_out[i] = cpu_to_be64(le64_to_cpu(data_in[i])); } -static int wm0010_boot(struct snd_soc_codec *codec) +static int wm0010_firmware_load(char *name, struct snd_soc_codec *codec) { struct spi_device *spi = to_spi_device(codec->dev); struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec); - unsigned long flags; struct list_head xfer_list; struct wm0010_boot_xfer *xfer; int ret; struct completion done; const struct firmware *fw; const struct dfw_binrec *rec; + u64 *img; + u8 *out, dsp; + u32 len, offset; + + INIT_LIST_HEAD(&xfer_list); + + ret = request_firmware(&fw, name, codec->dev); + if (ret != 0) { + dev_err(codec->dev, "Failed to request application: %d\n", + ret); + return ret; + } + + rec = (const struct dfw_binrec *)fw->data; + offset = 0; + dsp = rec->data[0]; + wm0010->boot_failed = false; + BUG_ON(!list_empty(&xfer_list)); + init_completion(&done); + + /* First record should be INFO */ + if (rec->command != DFW_CMD_INFO) { + dev_err(codec->dev, "First record not INFO\r\n"); + ret = -EINVAL; + goto abort; + } + + /* Check it's a DSP file */ + if (dsp != DEVICE_ID_WM0010) { + dev_err(codec->dev, "Not a WM0010 firmware file.\r\n"); + ret = -EINVAL; + goto abort; + } + + /* Skip the info record as we don't need to send it */ + offset += ((rec->length) + 8); + rec = (void *)&rec->data[rec->length]; + + while (offset < fw->size) { + dev_dbg(codec->dev, + "Packet: command %d, data length = 0x%x\r\n", + rec->command, rec->length); + len = rec->length + 8; + + out = kzalloc(len, GFP_KERNEL); + if (!out) { + dev_err(codec->dev, + "Failed to allocate RX buffer\n"); + ret = -ENOMEM; + goto abort1; + } + + img = kzalloc(len, GFP_KERNEL); + if (!img) { + dev_err(codec->dev, + "Failed to allocate image buffer\n"); + ret = -ENOMEM; + goto abort1; + } + + byte_swap_64((u64 *)&rec->command, img, len); + + xfer = kzalloc(sizeof(*xfer), GFP_KERNEL); + if (!xfer) { + dev_err(codec->dev, "Failed to allocate xfer\n"); + ret = -ENOMEM; + goto abort1; + } + + xfer->codec = codec; + list_add_tail(&xfer->list, &xfer_list); + + spi_message_init(&xfer->m); + xfer->m.complete = wm0010_boot_xfer_complete; + xfer->m.context = xfer; + xfer->t.tx_buf = img; + xfer->t.rx_buf = out; + xfer->t.len = len; + xfer->t.bits_per_word = 8; + + if (!wm0010->pll_running) { + xfer->t.speed_hz = wm0010->sysclk / 6; + } else { + xfer->t.speed_hz = wm0010->max_spi_freq; + + if (wm0010->board_max_spi_speed && + (wm0010->board_max_spi_speed < wm0010->max_spi_freq)) + xfer->t.speed_hz = wm0010->board_max_spi_speed; + } + + /* Store max usable spi frequency for later use */ + wm0010->max_spi_freq = xfer->t.speed_hz; + + spi_message_add_tail(&xfer->t, &xfer->m); + + offset += ((rec->length) + 8); + rec = (void *)&rec->data[rec->length]; + + if (offset >= fw->size) { + dev_dbg(codec->dev, "All transfers scheduled\n"); + xfer->done = &done; + } + + ret = spi_async(spi, &xfer->m); + if (ret != 0) { + dev_err(codec->dev, "Write failed: %d\n", ret); + goto abort1; + } + + if (wm0010->boot_failed) { + dev_dbg(codec->dev, "Boot fail!\n"); + ret = -EINVAL; + goto abort1; + } + } + + wait_for_completion(&done); + + ret = 0; + +abort1: + while (!list_empty(&xfer_list)) { + xfer = list_first_entry(&xfer_list, struct wm0010_boot_xfer, + list); + kfree(xfer->t.rx_buf); + kfree(xfer->t.tx_buf); + list_del(&xfer->list); + kfree(xfer); + } + +abort: + release_firmware(fw); + return ret; +} + +static int wm0010_boot(struct snd_soc_codec *codec) +{ + struct spi_device *spi = to_spi_device(codec->dev); + struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec); + unsigned long flags; + int ret; + const struct firmware *fw; struct spi_message m; struct spi_transfer t; struct dfw_pllrec pll_rec; u32 *img, *p; u64 *img_swap; u8 *out; - u32 len, offset; + u32 len; int i; spin_lock_irqsave(&wm0010->irq_lock, flags); @@ -363,8 +504,6 @@ static int wm0010_boot(struct snd_soc_codec *codec) goto err; } - INIT_LIST_HEAD(&xfer_list); - mutex_lock(&wm0010->lock); wm0010->pll_running = false; @@ -533,109 +672,10 @@ static int wm0010_boot(struct snd_soc_codec *codec) } else dev_dbg(codec->dev, "Not enabling DSP PLL."); - ret = request_firmware(&fw, "wm0010.dfw", codec->dev); - if (ret != 0) { - dev_err(codec->dev, "Failed to request application: %d\n", - ret); + ret = wm0010_firmware_load("wm0010.dfw", codec); + + if (ret != 0) goto abort; - } - - rec = (const struct dfw_binrec *)fw->data; - offset = 0; - wm0010->boot_failed = false; - BUG_ON(!list_empty(&xfer_list)); - init_completion(&done); - - /* First record should be INFO */ - if (rec->command != DFW_CMD_INFO) { - dev_err(codec->dev, "First record not INFO\r\n"); - goto abort; - } - - /* Check it's a 0010 file */ - if (rec->data[0] != DEVICE_ID_WM0010) { - dev_err(codec->dev, "Not a WM0010 firmware file.\r\n"); - goto abort; - } - - /* Skip the info record as we don't need to send it */ - offset += ((rec->length) + 8); - rec = (void *)&rec->data[rec->length]; - - while (offset < fw->size) { - dev_dbg(codec->dev, - "Packet: command %d, data length = 0x%x\r\n", - rec->command, rec->length); - len = rec->length + 8; - - out = kzalloc(len, GFP_KERNEL); - if (!out) { - dev_err(codec->dev, - "Failed to allocate RX buffer\n"); - goto abort; - } - - img_swap = kzalloc(len, GFP_KERNEL); - if (!img_swap) { - dev_err(codec->dev, - "Failed to allocate image buffer\n"); - goto abort; - } - - /* We need to re-order for 0010 */ - byte_swap_64((u64 *)&rec->command, img_swap, len); - - xfer = kzalloc(sizeof(*xfer), GFP_KERNEL); - if (!xfer) { - dev_err(codec->dev, "Failed to allocate xfer\n"); - goto abort; - } - - xfer->codec = codec; - list_add_tail(&xfer->list, &xfer_list); - - spi_message_init(&xfer->m); - xfer->m.complete = wm0010_boot_xfer_complete; - xfer->m.context = xfer; - xfer->t.tx_buf = img_swap; - xfer->t.rx_buf = out; - xfer->t.len = len; - xfer->t.bits_per_word = 8; - - if (!wm0010->pll_running) { - xfer->t.speed_hz = wm0010->sysclk / 6; - } else { - xfer->t.speed_hz = wm0010->max_spi_freq; - - if (wm0010->board_max_spi_speed && - (wm0010->board_max_spi_speed < wm0010->max_spi_freq)) - xfer->t.speed_hz = wm0010->board_max_spi_speed; - } - - /* Store max usable spi frequency for later use */ - wm0010->max_spi_freq = xfer->t.speed_hz; - - spi_message_add_tail(&xfer->t, &xfer->m); - - offset += ((rec->length) + 8); - rec = (void *)&rec->data[rec->length]; - - if (offset >= fw->size) { - dev_dbg(codec->dev, "All transfers scheduled\n"); - xfer->done = &done; - } - - ret = spi_async(spi, &xfer->m); - if (ret != 0) { - dev_err(codec->dev, "Write failed: %d\n", ret); - goto abort; - } - - if (wm0010->boot_failed) - goto abort; - } - - wait_for_completion(&done); spin_lock_irqsave(&wm0010->irq_lock, flags); wm0010->state = WM0010_FIRMWARE; @@ -643,17 +683,6 @@ static int wm0010_boot(struct snd_soc_codec *codec) mutex_unlock(&wm0010->lock); - release_firmware(fw); - - while (!list_empty(&xfer_list)) { - xfer = list_first_entry(&xfer_list, struct wm0010_boot_xfer, - list); - kfree(xfer->t.rx_buf); - kfree(xfer->t.tx_buf); - list_del(&xfer->list); - kfree(xfer); - } - return 0; abort: From 3f5475df378cfcac51e9384153aef122555a60cb Mon Sep 17 00:00:00 2001 From: Scott Ling Date: Wed, 7 Nov 2012 16:53:18 +0000 Subject: [PATCH 3/6] ASoC: wm0010: Split out the stage2 load from the boot function Signed-off-by: Scott Ling Signed-off-by: Mark Brown --- sound/soc/codecs/wm0010.c | 129 +++++++++++++++++++++++--------------- 1 file changed, 79 insertions(+), 50 deletions(-) diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 7576330044ba..4b68ea86d7cb 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -477,6 +477,82 @@ abort: return ret; } +static int wm0010_stage2_load(struct snd_soc_codec *codec) +{ + struct spi_device *spi = to_spi_device(codec->dev); + struct wm0010_priv *wm0010 = snd_soc_codec_get_drvdata(codec); + const struct firmware *fw; + struct spi_message m; + struct spi_transfer t; + u32 *img; + u8 *out; + int i; + int ret = 0; + + ret = request_firmware(&fw, "wm0010_stage2.bin", codec->dev); + if (ret != 0) { + dev_err(codec->dev, "Failed to request stage2 loader: %d\n", + ret); + return ret; + } + + dev_dbg(codec->dev, "Downloading %zu byte stage 2 loader\n", fw->size); + + /* Copy to local buffer first as vmalloc causes problems for dma */ + img = kzalloc(fw->size, GFP_KERNEL); + if (!img) { + dev_err(codec->dev, "Failed to allocate image buffer\n"); + ret = -ENOMEM; + goto abort2; + } + + out = kzalloc(fw->size, GFP_KERNEL); + if (!out) { + dev_err(codec->dev, "Failed to allocate output buffer\n"); + ret = -ENOMEM; + goto abort1; + } + + memcpy(img, &fw->data[0], fw->size); + + spi_message_init(&m); + memset(&t, 0, sizeof(t)); + t.rx_buf = out; + t.tx_buf = img; + t.len = fw->size; + t.bits_per_word = 8; + t.speed_hz = wm0010->sysclk / 10; + spi_message_add_tail(&t, &m); + + dev_dbg(codec->dev, "Starting initial download at %dHz\n", + t.speed_hz); + + ret = spi_sync(spi, &m); + if (ret != 0) { + dev_err(codec->dev, "Initial download failed: %d\n", ret); + goto abort; + } + + /* Look for errors from the boot ROM */ + for (i = 0; i < fw->size; i++) { + if (out[i] != 0x55) { + dev_err(codec->dev, "Boot ROM error: %x in %d\n", + out[i], i); + wm0010_mark_boot_failure(wm0010); + ret = -EBUSY; + goto abort; + } + } +abort: + kfree(out); +abort1: + kfree(img); +abort2: + release_firmware(fw); + + return ret; +} + static int wm0010_boot(struct snd_soc_codec *codec) { struct spi_device *spi = to_spi_device(codec->dev); @@ -487,10 +563,9 @@ static int wm0010_boot(struct snd_soc_codec *codec) struct spi_message m; struct spi_transfer t; struct dfw_pllrec pll_rec; - u32 *img, *p; + u32 *p, len; u64 *img_swap; u8 *out; - u32 len; int i; spin_lock_irqsave(&wm0010->irq_lock, flags); @@ -546,55 +621,9 @@ static int wm0010_boot(struct snd_soc_codec *codec) wm0010->state = WM0010_BOOTROM; spin_unlock_irqrestore(&wm0010->irq_lock, flags); - dev_dbg(codec->dev, "Downloading %zu byte stage 2 loader\n", fw->size); - - /* Copy to local buffer first as vmalloc causes problems for dma */ - img = kzalloc(fw->size, GFP_KERNEL); - if (!img) { - dev_err(codec->dev, "Failed to allocate image buffer\n"); + ret = wm0010_stage2_load(codec); + if (ret) goto abort; - } - - out = kzalloc(fw->size, GFP_KERNEL); - if (!out) { - dev_err(codec->dev, "Failed to allocate output buffer\n"); - goto abort; - } - - memcpy(img, &fw->data[0], fw->size); - - spi_message_init(&m); - memset(&t, 0, sizeof(t)); - t.rx_buf = out; - t.tx_buf = img; - t.len = fw->size; - t.bits_per_word = 8; - t.speed_hz = wm0010->sysclk / 10; - spi_message_add_tail(&t, &m); - - dev_dbg(codec->dev, "Starting initial download at %dHz\n", - t.speed_hz); - - ret = spi_sync(spi, &m); - if (ret != 0) { - dev_err(codec->dev, "Initial download failed: %d\n", ret); - goto abort; - } - - /* Look for errors from the boot ROM */ - for (i = 0; i < fw->size; i++) { - if (out[i] != 0x55) { - ret = -EBUSY; - dev_err(codec->dev, "Boot ROM error: %x in %d\n", - out[i], i); - wm0010_mark_boot_failure(wm0010); - goto abort; - } - } - - release_firmware(fw); - kfree(img); - kfree(out); if (!wait_for_completion_timeout(&wm0010->boot_completion, msecs_to_jiffies(10))) From 631fcab2eafc38a1415165ceaa217494c3f70ba5 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Wed, 21 Nov 2012 10:47:44 +0000 Subject: [PATCH 4/6] ASoC: wm0010: Up the completion timeout to 20ms instead of 10ms Increase timeout to be more reliable and avoid the chance of missing interrupts during boot. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- sound/soc/codecs/wm0010.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 4b68ea86d7cb..8ebb8da88f73 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -614,7 +614,7 @@ static int wm0010_boot(struct snd_soc_codec *codec) } if (!wait_for_completion_timeout(&wm0010->boot_completion, - msecs_to_jiffies(10))) + msecs_to_jiffies(20))) dev_err(codec->dev, "Failed to get interrupt from DSP\n"); spin_lock_irqsave(&wm0010->irq_lock, flags); @@ -626,7 +626,7 @@ static int wm0010_boot(struct snd_soc_codec *codec) goto abort; if (!wait_for_completion_timeout(&wm0010->boot_completion, - msecs_to_jiffies(10))) + msecs_to_jiffies(20))) dev_err(codec->dev, "Failed to get interrupt from DSP loader.\n"); spin_lock_irqsave(&wm0010->irq_lock, flags); From 68e1969ea705dafcee721f02c4fbc34466917f83 Mon Sep 17 00:00:00 2001 From: Scott Ling Date: Fri, 23 Nov 2012 12:37:35 +0000 Subject: [PATCH 5/6] ASoC: wm0010: Add checking for .dfw info record version. The info record at the start of the dsp firmware file has been expanded to incorporate additional version information. We need to check the version to make sure we understand the layout of the information in the record. The srec2image tool is currently used to create this record during creation of the .dfw file. Signed-off-by: Scott Ling Signed-off-by: Mark Brown --- sound/soc/codecs/wm0010.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 8ebb8da88f73..3c3a7b16bb8b 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -31,6 +31,9 @@ #define DEVICE_ID_WM0010 10 +/* We only support v1 of the .dfw INFO record */ +#define INFO_VERSION 1 + enum dfw_cmd { DFW_CMD_FUSE = 0x01, DFW_CMD_CODE_HDR, @@ -46,6 +49,13 @@ struct dfw_binrec { uint8_t data[0]; } __packed; +struct dfw_inforec { + u8 info_version; + u8 tool_major_version; + u8 tool_minor_version; + u8 dsp_target; +}; + struct dfw_pllrec { u8 command; u32 length:24; @@ -342,6 +352,7 @@ static int wm0010_firmware_load(char *name, struct snd_soc_codec *codec) struct completion done; const struct firmware *fw; const struct dfw_binrec *rec; + const struct dfw_inforec *inforec; u64 *img; u8 *out, dsp; u32 len, offset; @@ -356,8 +367,9 @@ static int wm0010_firmware_load(char *name, struct snd_soc_codec *codec) } rec = (const struct dfw_binrec *)fw->data; + inforec = (const struct dfw_inforec *)rec->data; offset = 0; - dsp = rec->data[0]; + dsp = inforec->dsp_target; wm0010->boot_failed = false; BUG_ON(!list_empty(&xfer_list)); init_completion(&done); @@ -369,6 +381,17 @@ static int wm0010_firmware_load(char *name, struct snd_soc_codec *codec) goto abort; } + if (inforec->info_version != INFO_VERSION) { + dev_err(codec->dev, + "Unsupported version (%02d) of INFO record\r\n", + inforec->info_version); + ret = -EINVAL; + goto abort; + } + + dev_dbg(codec->dev, "Version v%02d INFO record found\r\n", + inforec->info_version); + /* Check it's a DSP file */ if (dsp != DEVICE_ID_WM0010) { dev_err(codec->dev, "Not a WM0010 firmware file.\r\n"); From 5d910966d24b755c2ad2231e934fc71af55a1ee7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 8 Oct 2012 14:48:17 +0900 Subject: [PATCH 6/6] ASoC: wm0010: Ignore interrupts in power down mode The device should not be generating interrupts when it does not have power so ignore incoming interrupts. Signed-off-by: Mark Brown --- sound/soc/codecs/wm0010.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 3c3a7b16bb8b..40256b526259 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -862,7 +862,6 @@ static irqreturn_t wm0010_irq(int irq, void *data) struct wm0010_priv *wm0010 = data; switch (wm0010->state) { - case WM0010_POWER_OFF: case WM0010_OUT_OF_RESET: case WM0010_BOOTROM: case WM0010_STAGE2: