OSDN Git Service

greybus: gb-audio: Set I2S Configuration according to ASOC requests
authorMark A. Greer <mgreer@animalcreek.com>
Thu, 21 May 2015 22:57:03 +0000 (15:57 -0700)
committerGreg Kroah-Hartman <gregkh@google.com>
Sat, 23 May 2015 23:26:41 +0000 (16:26 -0700)
Currently, the audio driver unconditionally sets the I2S
configuration to have a sample rate of 48KHz, two channels,
16 bits per channel, in little endian order.  Make this
more flexible by setting the I2S configuration according to
the arguments passed to the PCM 'hw_params' callback.

To accomplish this, query for the supported I2S configurations
at Greybus protocol init time and save them in the 'snd_dev'
structure.  When the 'hw_params' callback is called, compare its
arguments to the table of supported configurations.  If there is
a match, set the I2S connection accordingly.

Signed-off-by: Mark A. Greer <mgreer@animalcreek.com>
Acked-by: John Stultz <john.stultz@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
drivers/staging/greybus/audio-gb-cmds.c
drivers/staging/greybus/audio-pcm.c
drivers/staging/greybus/audio.c
drivers/staging/greybus/audio.h

index d625f78..73f47d8 100644 (file)
@@ -89,21 +89,12 @@ int gb_i2s_mgmt_set_samples_per_message(
                                 &request, sizeof(request), NULL, 0);
 }
 
-/*
- * XXX This is sort of a generic "setup" function which  probably needs
- * to be broken up, and tied into the constraints.
- *
- * I'm on the fence if we should just dictate that we only support
- * 48k, 16bit, 2 channel, and avoid doign the whole probe for configurations
- * and then picking one.
- */
-int gb_i2s_mgmt_setup(struct gb_connection *connection)
+int gb_i2s_mgmt_get_cfgs(struct gb_snd *snd_dev,
+                        struct gb_connection *connection)
 {
        struct gb_i2s_mgmt_get_supported_configurations_response *get_cfg;
-       struct gb_i2s_mgmt_set_configuration_request set_cfg;
-       struct gb_i2s_mgmt_configuration *cfg;
        size_t size;
-       int i, ret;
+       int ret;
 
        size = sizeof(*get_cfg) +
               (CONFIG_COUNT_MAX * sizeof(get_cfg->config[0]));
@@ -116,19 +107,48 @@ int gb_i2s_mgmt_setup(struct gb_connection *connection)
                                                       size);
        if (ret) {
                pr_err("get_supported_config failed: %d\n", ret);
-               goto free_get_cfg;
+               goto err_free_get_cfg;
        }
 
-       /* Pick 48KHz 16-bits/channel */
-       for (i = 0, cfg = get_cfg->config; i < CONFIG_COUNT_MAX; i++, cfg++) {
-               if ((le32_to_cpu(cfg->sample_frequency) == GB_SAMPLE_RATE) &&
-                   (cfg->num_channels == 2) &&
-                   (cfg->bytes_per_channel == 2) &&
-                   (cfg->byte_order & GB_I2S_MGMT_BYTE_ORDER_LE) &&
-                   (le32_to_cpu(cfg->spatial_locations) ==
-                       (GB_I2S_MGMT_SPATIAL_LOCATION_FL |
-                        GB_I2S_MGMT_SPATIAL_LOCATION_FR)) &&
-                   (le32_to_cpu(cfg->ll_protocol) & GB_I2S_MGMT_PROTOCOL_I2S) &&
+       snd_dev->i2s_configs = get_cfg;
+
+       return 0;
+
+err_free_get_cfg:
+       kfree(get_cfg);
+       return ret;
+}
+
+void gb_i2s_mgmt_free_cfgs(struct gb_snd *snd_dev)
+{
+       kfree(snd_dev->i2s_configs);
+       snd_dev->i2s_configs = NULL;
+}
+
+int gb_i2s_mgmt_set_cfg(struct gb_snd *snd_dev, int rate, int chans,
+                       int bytes_per_chan, int is_le)
+{
+       struct gb_i2s_mgmt_set_configuration_request set_cfg;
+       struct gb_i2s_mgmt_configuration *cfg;
+       int i, ret;
+       u8 byte_order = GB_I2S_MGMT_BYTE_ORDER_NA;
+
+       if (bytes_per_chan > 1) {
+               if (is_le)
+                       byte_order = GB_I2S_MGMT_BYTE_ORDER_LE;
+               else
+                       byte_order = GB_I2S_MGMT_BYTE_ORDER_BE;
+       }
+
+       for (i = 0, cfg = snd_dev->i2s_configs->config;
+            i < CONFIG_COUNT_MAX;
+            i++, cfg++) {
+               if ((cfg->sample_frequency == cpu_to_le32(rate)) &&
+                   (cfg->num_channels == chans) &&
+                   (cfg->bytes_per_channel == bytes_per_chan) &&
+                   (cfg->byte_order & byte_order) &&
+                   (cfg->ll_protocol &
+                            cpu_to_le32(GB_I2S_MGMT_PROTOCOL_I2S)) &&
                    (cfg->ll_mclk_role & GB_I2S_MGMT_ROLE_MASTER) &&
                    (cfg->ll_bclk_role & GB_I2S_MGMT_ROLE_MASTER) &&
                    (cfg->ll_wclk_role & GB_I2S_MGMT_ROLE_MASTER) &&
@@ -142,12 +162,11 @@ int gb_i2s_mgmt_setup(struct gb_connection *connection)
 
        if (i >= CONFIG_COUNT_MAX) {
                pr_err("No valid configuration\n");
-               ret = -EINVAL;
-               goto free_get_cfg;
+               return -EINVAL;
        }
 
        memcpy(&set_cfg, cfg, sizeof(set_cfg));
-       set_cfg.config.byte_order = GB_I2S_MGMT_BYTE_ORDER_LE;
+       set_cfg.config.byte_order = byte_order;
        set_cfg.config.ll_protocol = cpu_to_le32(GB_I2S_MGMT_PROTOCOL_I2S);
        set_cfg.config.ll_mclk_role = GB_I2S_MGMT_ROLE_MASTER;
        set_cfg.config.ll_bclk_role = GB_I2S_MGMT_ROLE_MASTER;
@@ -157,19 +176,17 @@ int gb_i2s_mgmt_setup(struct gb_connection *connection)
        set_cfg.config.ll_wclk_tx_edge = GB_I2S_MGMT_EDGE_RISING;
        set_cfg.config.ll_wclk_rx_edge = GB_I2S_MGMT_EDGE_FALLING;
 
-       ret = gb_i2s_mgmt_set_configuration(connection, &set_cfg);
+       ret = gb_i2s_mgmt_set_configuration(snd_dev->mgmt_connection, &set_cfg);
        if (ret) {
                pr_err("set_configuration failed: %d\n", ret);
-               goto free_get_cfg;
+               return ret;
        }
 
-       ret = gb_i2s_mgmt_set_samples_per_message(connection,
+       ret = gb_i2s_mgmt_set_samples_per_message(snd_dev->mgmt_connection,
                                                  CONFIG_SAMPLES_PER_MSG);
        if (ret)
                pr_err("set_samples_per_msg failed: %d\n", ret);
 
-free_get_cfg:
-       kfree(get_cfg);
        return ret;
 }
 
index 30030f8..b327008 100644 (file)
@@ -220,6 +220,21 @@ static int gb_pcm_close(struct snd_pcm_substream *substream)
 static int gb_pcm_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *hw_params)
 {
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct gb_snd *snd_dev;
+       int rate, chans, bytes_per_chan, is_le, ret;
+
+       snd_dev = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+       rate = params_rate(hw_params);
+       chans = params_channels(hw_params);
+       bytes_per_chan = snd_pcm_format_width(params_format(hw_params)) / 8;
+       is_le = snd_pcm_format_little_endian(params_format(hw_params));
+
+       ret = gb_i2s_mgmt_set_cfg(snd_dev, rate, chans, bytes_per_chan, is_le);
+       if (ret)
+               return ret;
+
        return snd_pcm_lib_malloc_pages(substream,
                                        params_buffer_bytes(hw_params));
 }
index 1057e46..a1acbb0 100644 (file)
@@ -286,17 +286,23 @@ static int gb_i2s_mgmt_connection_init(struct gb_connection *connection)
                goto err_free_snd_dev;
        }
 
-       gb_i2s_mgmt_setup(connection);
+       ret = gb_i2s_mgmt_get_cfgs(snd_dev, connection);
+       if (ret) {
+               pr_err("can't get i2s configurations: %d\n", ret);
+               goto err_free_snd_dev;
+       }
 
        snd_dev->send_data_req_buf = kzalloc(SEND_DATA_BUF_LEN, GFP_KERNEL);
 
        if (!snd_dev->send_data_req_buf) {
                ret = -ENOMEM;
-               goto err_free_snd_dev;
+               goto err_free_i2s_configs;
        }
 
        return 0;
 
+err_free_i2s_configs:
+       gb_i2s_mgmt_free_cfgs(snd_dev);
 err_free_snd_dev:
        gb_free_snd(snd_dev);
        return ret;
@@ -306,6 +312,8 @@ static void gb_i2s_mgmt_connection_exit(struct gb_connection *connection)
 {
        struct gb_snd *snd_dev = (struct gb_snd *)connection->private;
 
+       gb_i2s_mgmt_free_cfgs(snd_dev);
+
        kfree(snd_dev->send_data_req_buf);
        snd_dev->send_data_req_buf = NULL;
 
index 012c69a..fa1bb54 100644 (file)
@@ -45,6 +45,8 @@ struct gb_snd {
        struct gb_connection            *mgmt_connection;
        struct gb_connection            *i2s_tx_connection;
        struct gb_connection            *i2s_rx_connection;
+       struct gb_i2s_mgmt_get_supported_configurations_response
+                                       *i2s_configs;
        char                            *send_data_req_buf;
        long                            send_data_sample_count;
        int                             gb_bundle_id;
@@ -79,7 +81,11 @@ int gb_i2s_mgmt_set_configuration(struct gb_connection *connection,
                        struct gb_i2s_mgmt_set_configuration_request *set_cfg);
 int gb_i2s_mgmt_set_samples_per_message(struct gb_connection *connection,
                                        uint16_t samples_per_message);
-int gb_i2s_mgmt_setup(struct gb_connection *connection);
+int gb_i2s_mgmt_get_cfgs(struct gb_snd *snd_dev,
+                        struct gb_connection *connection);
+void gb_i2s_mgmt_free_cfgs(struct gb_snd *snd_dev);
+int gb_i2s_mgmt_set_cfg(struct gb_snd *snd_dev, int rate, int chans,
+                       int bytes_per_chan, int is_le);
 int gb_i2s_send_data(struct gb_connection *connection, void *req_buf,
                                void *source_addr, size_t len, int sample_num);