[alsa-devel] [PATCH 5/8] ALSA: Update CA0132 codec to load DSP firmware binary
Takashi Iwai
tiwai at suse.de
Fri Sep 14 10:52:05 CEST 2012
At Thu, 13 Sep 2012 18:15:55 -0700,
Ian Minett wrote:
>
> From: Ian Minett <ian_minett at creativelabs.com>
>
Give a bit more description what this patch really does and also what
it doesn't. This just implements the firmware loading, but the new
DSP functionality itself isn't used yet, right?
> Signed-off-by: Ian Minett <ian_minett at creativelabs.com>
>
> diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
> index 8ea3348..11f5910 100644
> --- a/sound/pci/hda/patch_ca0132.c
> +++ b/sound/pci/hda/patch_ca0132.c
> @@ -48,9 +48,6 @@
> #define WIDGET_CHIP_CTRL 0x15
> #define WIDGET_DSP_CTRL 0x16
>
> -#define WUH_MEM_CONNID 10
> -#define DSP_MEM_CONNID 16
> -
> #define MEM_CONNID_MICIN1 3
> #define MEM_CONNID_MICIN2 5
> #define MEM_CONNID_MICOUT1 12
> @@ -352,6 +349,25 @@ static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
> #define add_in_mono_volume(codec, nid, pfx, chan) \
> _add_volume(codec, nid, pfx, chan, 1)
>
> +enum dsp_download_state {
> + DSP_DOWNLOAD_FAILED = -1,
> + DSP_DOWNLOAD_INIT = 0,
> + DSP_DOWNLOADING = 1,
> + DSP_DOWNLOADED = 2
> +};
> +
> +struct hda_stream_format {
> + unsigned int sample_rate;
> + unsigned short valid_bits_per_sample;
> + unsigned short container_size;
> + unsigned short number_channels;
> +};
> +
> +/* retrieve parameters from hda format */
> +#define get_hdafmt_chs(fmt) (fmt & 0xf)
> +#define get_hdafmt_bits(fmt) ((fmt >> 4) & 0x7)
> +#define get_hdafmt_rate(fmt) ((fmt >> 8) & 0x7f)
> +#define get_hdafmt_type(fmt) ((fmt >> 15) & 0x1)
This can be well in hda_codec.h. But it's fine to define here for
now...
> /*
> * CA0132 specific
> @@ -371,11 +387,55 @@ struct ca0132_spec {
> long curr_hp_switch;
> long curr_hp_volume[2];
> long curr_speaker_switch;
> - struct mutex chipio_mutex;
> const char *input_labels[AUTO_PIN_LAST];
> struct hda_pcm pcm_rec[2]; /* PCM information */
> +
> + /* chip access */
> + struct mutex chipio_mutex; /* chip access mutex */
> + u32 curr_chip_addx;
> +
> + /* DSP download related */
> + enum dsp_download_state dsp_state;
> + unsigned int dsp_stream_id;
> + unsigned int wait_scp;
> + unsigned int wait_scp_header;
> + unsigned int wait_num_data;
> + unsigned int scp_resp_header;
> + unsigned int scp_resp_data[4];
> + unsigned int scp_resp_count;
> };
>
> +/*
> + * CA0132 codec access
> + */
> +unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid,
> + unsigned int verb, unsigned int parm, unsigned int *res)
> +{
> + unsigned int response;
> + response = snd_hda_codec_read(codec, nid, 0, verb, parm);
> + *res = response;
> +
> + return ((response == -1) ? -1 : 0);
> +}
> +
> +static int codec_set_converter_format(struct hda_codec *codec, hda_nid_t nid,
> + unsigned short converter_format, unsigned int *res)
> +{
> + return codec_send_command(codec, nid, VENDOR_CHIPIO_STREAM_FORMAT,
> + converter_format & 0xffff, res);
> +}
> +
> +static int codec_set_converter_stream_channel(struct hda_codec *codec,
> + hda_nid_t nid, unsigned char stream,
> + unsigned char channel, unsigned int *res)
> +{
> + unsigned char converter_stream_channel = 0;
> +
> + converter_stream_channel = (stream << 4) | (channel & 0x0f);
> + return codec_send_command(codec, nid, AC_VERB_SET_CHANNEL_STREAMID,
> + converter_stream_channel, res);
> +}
> +
> /* Chip access helper function */
> static int chipio_send(struct hda_codec *codec,
> unsigned int reg,
> @@ -415,6 +475,30 @@ static int chipio_write_address(struct hda_codec *codec,
> return res;
> }
>
> +static int chipio_write_addx(struct hda_codec *codec, u32 chip_addx)
> +{
> + struct ca0132_spec *spec = codec->spec;
> + int status;
> +
> + if (spec->curr_chip_addx == chip_addx)
> + return 0;
> +
> + /* send low 16 bits of the address */
> + status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
> + chip_addx & 0xffff);
> +
> + if (status < 0)
> + return status;
> +
> + /* send high 16 bits of the address */
> + status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH,
> + chip_addx >> 16);
> +
> + spec->curr_chip_addx = (status < 0) ? ~0UL : chip_addx;
> +
> + return status;
> +}
> +
> /*
> * Write data through the vendor widget -- NOT protected by the Mutex!
> */
> @@ -435,6 +519,24 @@ static int chipio_write_data(struct hda_codec *codec, unsigned int data)
> return res;
> }
>
> +static int chipio_write_data_multiple(struct hda_codec *codec,
> + const u32 *data,
> + unsigned int count)
> +{
> + int status = 0;
> +
> + if (data == NULL) {
> + snd_printdd(KERN_ERR "chipio_write_data null ptr");
> + return -EINVAL;
> + }
> +
> + while ((count-- != 0) && (status == 0))
> + status = chipio_write_data(codec, *data++);
> +
> + return status;
> +}
> +
> +
> /*
> * Read data through the vendor widget -- NOT protected by the Mutex!
> */
> @@ -486,6 +588,26 @@ exit:
> return err;
> }
>
> +static int chipio_write_multiple(struct hda_codec *codec,
> + u32 chip_addx,
> + const u32 *data,
> + unsigned int count)
> +{
> + struct ca0132_spec *spec = codec->spec;
> + int status;
> +
> + mutex_lock(&spec->chipio_mutex);
> + status = chipio_write_addx(codec, chip_addx);
> + if (status < 0)
> + goto error;
> +
> + status = chipio_write_data_multiple(codec, data, count);
> +error:
> + mutex_unlock(&spec->chipio_mutex);
> +
> + return status;
> +}
> +
> /*
> * Read the given address through the chip I/O widget
> * protected by the Mutex
> @@ -512,6 +634,1425 @@ exit:
> return err;
> }
>
> +static void chipio_set_control_flag(struct hda_codec *codec,
> + enum control_flag_id flag_id,
> + bool flag_state)
> +{
> + unsigned int val;
> + unsigned int flag_bit;
> +
> + flag_bit = (flag_state ? 1 : 0);
> + val = (flag_bit << 7) | (flag_id);
> + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> + VENDOR_CHIPIO_FLAG_SET, val);
> +}
> +
> +static void chipio_set_control_param(struct hda_codec *codec,
> + enum control_param_id param_id, int param_val)
> +{
> + struct ca0132_spec *spec = codec->spec;
> + int val;
> +
> + if ((param_id < 32) && (param_val < 8)) {
> + val = (param_val << 5) | (param_id);
> + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> + VENDOR_CHIPIO_PARAM_SET, val);
> + } else {
> + mutex_lock(&spec->chipio_mutex);
> + if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) {
> + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> + VENDOR_CHIPIO_PARAM_EX_ID_SET,
> + param_id);
> + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> + VENDOR_CHIPIO_PARAM_EX_VALUE_SET,
> + param_val);
> + }
> + mutex_unlock(&spec->chipio_mutex);
> + }
> +}
> +
> +static void chipio_set_conn_rate(struct hda_codec *codec,
> + int connid, enum ca0132_sample_rate rate)
> +{
> + chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_ID, connid);
> + chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_SAMPLE_RATE,
> + rate);
> +}
> +
> +static void chipio_enable_clocks(struct hda_codec *codec)
> +{
> + struct ca0132_spec *spec = codec->spec;
> +
> + mutex_lock(&spec->chipio_mutex);
> + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0);
> + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff);
> + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> + VENDOR_CHIPIO_8051_ADDRESS_LOW, 5);
> + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> + VENDOR_CHIPIO_PLL_PMU_WRITE, 0x0b);
> + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> + VENDOR_CHIPIO_8051_ADDRESS_LOW, 6);
> + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff);
> + mutex_unlock(&spec->chipio_mutex);
> +}
> +
> +/*
> + * CA0132 DSP IO stuffs
> + */
> +static int dspio_send(struct hda_codec *codec, unsigned int reg,
> + unsigned int data)
> +{
> + unsigned int res;
> + int retry = 50;
> +
> + /* send bits of data specified by reg to dsp */
> + do {
> + res = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, reg, data);
> + if ((res >= 0) && (res != VENDOR_STATUS_DSPIO_BUSY))
> + return res;
> + } while (--retry);
> +
> + return -EIO;
> +}
> +
> +static void dspio_write_wait(struct hda_codec *codec)
> +{
> + int cur_val, prv_val;
> + int retry = 50;
> +
> + cur_val = 0;
> + do {
> + prv_val = cur_val;
> + msleep(20);
> + dspio_send(codec, VENDOR_DSPIO_SCP_POST_COUNT_QUERY, 1);
> + dspio_send(codec, VENDOR_DSPIO_STATUS, 0);
> + cur_val = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
> + VENDOR_DSPIO_SCP_READ_COUNT, 0);
> + } while (cur_val && (cur_val == prv_val) && --retry);
> +}
> +
> +static int dspio_write(struct hda_codec *codec, unsigned int scp_data)
> +{
> + struct ca0132_spec *spec = codec->spec;
> + int status;
> +
> + dspio_write_wait(codec);
> +
> + mutex_lock(&spec->chipio_mutex);
> + status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_LOW,
> + scp_data & 0xffff);
> + if (status < 0)
> + goto error;
> +
> + status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_HIGH,
> + scp_data >> 16);
> + if (status < 0)
> + goto error;
> +
> + /* OK, now check if the write itself has executed*/
> + status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
> + VENDOR_DSPIO_STATUS, 0);
> +error:
> + mutex_unlock(&spec->chipio_mutex);
> +
> + return (status == VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL) ?
> + -EIO : 0;
> +}
> +
> +static int dspio_write_multiple(struct hda_codec *codec,
> + unsigned int *buffer, unsigned int size)
> +{
> + int status = 0;
> + unsigned int count;
> +
> + if ((buffer == NULL))
> + return -EINVAL;
> +
> + count = 0;
> + while (count < size) {
> + status = dspio_write(codec, *buffer++);
> + if (status != 0)
> + break;
> + count++;
> + }
> +
> + return status;
> +}
> +
> +static inline unsigned int
> +make_scp_header(unsigned int target_id, unsigned int source_id,
> + unsigned int get_flag, unsigned int req,
> + unsigned int device_flag, unsigned int resp_flag,
> + unsigned int error_flag, unsigned int data_size)
> +{
> + unsigned int header = 0;
> +
> + header = (data_size & 0x1f) << 27;
> + header |= (error_flag & 0x01) << 26;
> + header |= (resp_flag & 0x01) << 25;
> + header |= (device_flag & 0x01) << 24;
> + header |= (req & 0x7f) << 17;
> + header |= (get_flag & 0x01) << 16;
> + header |= (source_id & 0xff) << 8;
> + header |= target_id & 0xff;
> +
> + return header;
> +}
> +
> +static inline void
> +extract_scp_header(unsigned int header,
> + unsigned int *target_id, unsigned int *source_id,
> + unsigned int *get_flag, unsigned int *req,
> + unsigned int *device_flag, unsigned int *resp_flag,
> + unsigned int *error_flag, unsigned int *data_size)
> +{
> + if (data_size)
> + *data_size = (header >> 27) & 0x1f;
> + if (error_flag)
> + *error_flag = (header >> 26) & 0x01;
> + if (resp_flag)
> + *resp_flag = (header >> 25) & 0x01;
> + if (device_flag)
> + *device_flag = (header >> 24) & 0x01;
> + if (req)
> + *req = (header >> 17) & 0x7f;
> + if (get_flag)
> + *get_flag = (header >> 16) & 0x01;
> + if (source_id)
> + *source_id = (header >> 8) & 0xff;
> + if (target_id)
> + *target_id = header & 0xff;
> +}
> +
> +#define SCP_MAX_DATA_WORDS (16)
> +
> +/* Structure to contain any SCP message */
> +struct scp_msg {
> + unsigned int hdr;
> + unsigned int data[SCP_MAX_DATA_WORDS];
> +};
> +
> +static int dspio_send_scp_message(struct hda_codec *codec,
> + unsigned char *send_buf,
> + unsigned int send_buf_size,
> + unsigned char *return_buf,
> + unsigned int return_buf_size,
> + unsigned int *bytes_returned)
> +{
> + struct ca0132_spec *spec = codec->spec;
> + int retry;
> + int status = -1;
> + unsigned int scp_send_size = 0;
> + unsigned int total_size;
> + bool waiting_for_resp = false;
> + unsigned int header;
> + struct scp_msg *ret_msg;
> + unsigned int resp_src_id, resp_target_id;
> + unsigned int data_size, src_id, target_id, get_flag, device_flag;
> +
> + if (bytes_returned)
> + *bytes_returned = 0;
> +
> + /* get scp header from buffer */
> + header = *((unsigned int *)send_buf);
This is not quite portable. The HD-audio itself might be implemented
in big-endian machines, too.
Or, this might be more about struct scp_msg definition.
What is the expectation from the hardware? Does the codec expect
32bit little endian values? Then you'd need to convert data
explicitly when the architecture is big-endian.
(snip)
> static int ca0132_init(struct hda_codec *codec)
> {
> struct ca0132_spec *spec = codec->spec;
> struct auto_pin_cfg *cfg = &spec->autocfg;
> int i;
>
> + ca0132_download_dsp(codec);
> +
As I mentioned earlier, you cannot implement the DSP loading code
unconditionally. User might not have a DSP firmware but only updated
the kernel. Then it gets broken.
The DSP loading code (and the relevant stuff) must be protected via
ifdef CONFIG_SND_HDA_DSP_LOADER. And yet, it wouldn't be bad to have
a module option to control the firmware loading as wel.
Takashi
More information about the Alsa-devel
mailing list