At Thu, 13 Sep 2012 18:15:55 -0700, Ian Minett wrote:
From: Ian Minett <ian_minett@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@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