/*
* wm8958-dsp2.c -- WM8958 DSP2 support
*
* Copyright 2011 Wolfson Microelectronics plc
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <trace/events/asoc.h>
#include <linux/mfd/wm8994/core.h>
#include <linux/mfd/wm8994/registers.h>
#include <linux/mfd/wm8994/pdata.h>
#include <linux/mfd/wm8994/gpio.h>
#include "wm8994.h"
#define WM_FW_BLOCK_INFO 0xff
#define WM_FW_BLOCK_PM 0x00
#define WM_FW_BLOCK_X 0x01
#define WM_FW_BLOCK_Y 0x02
#define WM_FW_BLOCK_Z 0x03
#define WM_FW_BLOCK_I 0x06
#define WM_FW_BLOCK_A 0x08
#define WM_FW_BLOCK_C 0x0c
static int wm8958_dsp2_fw(struct snd_soc_codec *codec, const char *name,
const struct firmware *fw, bool check)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
u64 data64;
u32 data32;
const u8 *data;
char *str;
size_t block_len, len;
int ret = 0;
/* Suppress unneeded downloads */
if (wm8994->cur_fw == fw)
return 0;
if (fw->size < 32) {
dev_err(codec->dev, "%s: firmware too short\n", name);
goto err;
}
if (memcmp(fw->data, "WMFW", 4) != 0) {
dev_err(codec->dev, "%s: firmware has bad file magic %08x\n",
name, data32);
goto err;
}
memcpy(&data32, fw->data + 4, sizeof(data32));
len = be32_to_cpu(data32);
memcpy(&data32, fw->data + 8, sizeof(data32));
data32 = be32_to_cpu(data32);
if ((data32 >> 24) & 0xff) {
dev_err(codec->dev, "%s: unsupported firmware version %d\n",
name, (data32 >> 24) & 0xff);
goto err;
}
if ((data32 & 0xffff) != 8958) {
dev_err(codec->dev, "%s: unsupported target device %d\n",
name, data32 & 0xffff);
goto err;
}
if (((data32 >> 16) & 0xff) != 0xc) {
dev_err(codec->dev, "%s: unsupported target core %d\n",
name, (data32 >> 16) & 0xff);
goto err;
}
if (check) {
memcpy(&data64, fw->data + 24, sizeof(u64));
dev_info(codec->dev, "%s timestamp %llx\n",
name, be64_to_cpu(data64));
} else {
snd_soc_write(codec, 0x102, 0x2);
snd_soc_write(codec, 0x900, 0x2);
}
data = fw->data + len;
len = fw->size - len;
while (len) {
if (len < 12) {
dev_err(codec->dev, "%s short data block of %zd\n",
name, len);
goto err;
}
memcpy(&data32, data + 4, sizeof(data32));
block_len = be32_to_cpu(data32);
if (block_len + 8 > len) {
dev_err(codec->dev, "%zd byte block longer than file\n",
block_len);
goto err;
}
if (block_len == 0) {
dev_err(codec->dev, "Zero length block\n");
goto err;
}
memcpy(&data32, data, sizeof(data32));
data32 = be32_to_cpu(data32);
switch ((data32 >> 24) & 0xff) {
case WM_FW_BLOCK_INFO: