[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