Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 // Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
0003 // Copyright (c) 2018, Linaro Limited
0004 
0005 #include <linux/device.h>
0006 #include <linux/jiffies.h>
0007 #include <linux/kernel.h>
0008 #include <linux/kref.h>
0009 #include <linux/module.h>
0010 #include <linux/of.h>
0011 #include <linux/of_platform.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/sched.h>
0014 #include <linux/slab.h>
0015 #include <linux/soc/qcom/apr.h>
0016 #include <linux/wait.h>
0017 #include <sound/asound.h>
0018 #include "q6adm.h"
0019 #include "q6afe.h"
0020 #include "q6core.h"
0021 #include "q6dsp-common.h"
0022 #include "q6dsp-errno.h"
0023 
0024 #define ADM_CMD_DEVICE_OPEN_V5      0x00010326
0025 #define ADM_CMDRSP_DEVICE_OPEN_V5   0x00010329
0026 #define ADM_CMD_DEVICE_CLOSE_V5     0x00010327
0027 #define ADM_CMD_MATRIX_MAP_ROUTINGS_V5  0x00010325
0028 
0029 #define TIMEOUT_MS 1000
0030 #define RESET_COPP_ID 99
0031 #define INVALID_COPP_ID 0xFF
0032 /* Definition for a legacy device session. */
0033 #define ADM_LEGACY_DEVICE_SESSION   0
0034 #define ADM_MATRIX_ID_AUDIO_RX      0
0035 #define ADM_MATRIX_ID_AUDIO_TX      1
0036 
0037 struct q6copp {
0038     int afe_port;
0039     int copp_idx;
0040     int id;
0041     int topology;
0042     int mode;
0043     int rate;
0044     int bit_width;
0045     int channels;
0046     int app_type;
0047     int acdb_id;
0048 
0049     struct aprv2_ibasic_rsp_result_t result;
0050     struct kref refcount;
0051     wait_queue_head_t wait;
0052     struct list_head node;
0053     struct q6adm *adm;
0054 };
0055 
0056 struct q6adm {
0057     struct apr_device *apr;
0058     struct device *dev;
0059     struct q6core_svc_api_info ainfo;
0060     unsigned long copp_bitmap[AFE_MAX_PORTS];
0061     struct list_head copps_list;
0062     spinlock_t copps_list_lock;
0063     struct aprv2_ibasic_rsp_result_t result;
0064     struct mutex lock;
0065     wait_queue_head_t matrix_map_wait;
0066 };
0067 
0068 struct q6adm_cmd_device_open_v5 {
0069     u16 flags;
0070     u16 mode_of_operation;
0071     u16 endpoint_id_1;
0072     u16 endpoint_id_2;
0073     u32 topology_id;
0074     u16 dev_num_channel;
0075     u16 bit_width;
0076     u32 sample_rate;
0077     u8 dev_channel_mapping[8];
0078 } __packed;
0079 
0080 struct q6adm_cmd_matrix_map_routings_v5 {
0081     u32 matrix_id;
0082     u32 num_sessions;
0083 } __packed;
0084 
0085 struct q6adm_session_map_node_v5 {
0086     u16 session_id;
0087     u16 num_copps;
0088 } __packed;
0089 
0090 static struct q6copp *q6adm_find_copp(struct q6adm *adm, int port_idx,
0091                   int copp_idx)
0092 {
0093     struct q6copp *c;
0094     struct q6copp *ret = NULL;
0095     unsigned long flags;
0096 
0097     spin_lock_irqsave(&adm->copps_list_lock, flags);
0098     list_for_each_entry(c, &adm->copps_list, node) {
0099         if ((port_idx == c->afe_port) && (copp_idx == c->copp_idx)) {
0100             ret = c;
0101             kref_get(&c->refcount);
0102             break;
0103         }
0104     }
0105 
0106     spin_unlock_irqrestore(&adm->copps_list_lock, flags);
0107 
0108     return ret;
0109 
0110 }
0111 
0112 static void q6adm_free_copp(struct kref *ref)
0113 {
0114     struct q6copp *c = container_of(ref, struct q6copp, refcount);
0115     struct q6adm *adm = c->adm;
0116     unsigned long flags;
0117 
0118     spin_lock_irqsave(&adm->copps_list_lock, flags);
0119     clear_bit(c->copp_idx, &adm->copp_bitmap[c->afe_port]);
0120     list_del(&c->node);
0121     spin_unlock_irqrestore(&adm->copps_list_lock, flags);
0122     kfree(c);
0123 }
0124 
0125 static int q6adm_callback(struct apr_device *adev, struct apr_resp_pkt *data)
0126 {
0127     struct aprv2_ibasic_rsp_result_t *result = data->payload;
0128     int port_idx, copp_idx;
0129     struct apr_hdr *hdr = &data->hdr;
0130     struct q6copp *copp;
0131     struct q6adm *adm = dev_get_drvdata(&adev->dev);
0132 
0133     if (!data->payload_size)
0134         return 0;
0135 
0136     copp_idx = (hdr->token) & 0XFF;
0137     port_idx = ((hdr->token) >> 16) & 0xFF;
0138     if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) {
0139         dev_err(&adev->dev, "Invalid port idx %d token %d\n",
0140                port_idx, hdr->token);
0141         return 0;
0142     }
0143     if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
0144         dev_err(&adev->dev, "Invalid copp idx %d token %d\n",
0145             copp_idx, hdr->token);
0146         return 0;
0147     }
0148 
0149     switch (hdr->opcode) {
0150     case APR_BASIC_RSP_RESULT: {
0151         if (result->status != 0) {
0152             dev_err(&adev->dev, "cmd = 0x%x return error = 0x%x\n",
0153                 result->opcode, result->status);
0154         }
0155         switch (result->opcode) {
0156         case ADM_CMD_DEVICE_OPEN_V5:
0157         case ADM_CMD_DEVICE_CLOSE_V5:
0158             copp = q6adm_find_copp(adm, port_idx, copp_idx);
0159             if (!copp)
0160                 return 0;
0161 
0162             copp->result = *result;
0163             wake_up(&copp->wait);
0164             kref_put(&copp->refcount, q6adm_free_copp);
0165             break;
0166         case ADM_CMD_MATRIX_MAP_ROUTINGS_V5:
0167             adm->result = *result;
0168             wake_up(&adm->matrix_map_wait);
0169             break;
0170 
0171         default:
0172             dev_err(&adev->dev, "Unknown Cmd: 0x%x\n",
0173                 result->opcode);
0174             break;
0175         }
0176         return 0;
0177     }
0178     case ADM_CMDRSP_DEVICE_OPEN_V5: {
0179         struct adm_cmd_rsp_device_open_v5 {
0180             u32 status;
0181             u16 copp_id;
0182             u16 reserved;
0183         } __packed *open = data->payload;
0184 
0185         copp = q6adm_find_copp(adm, port_idx, copp_idx);
0186         if (!copp)
0187             return 0;
0188 
0189         if (open->copp_id == INVALID_COPP_ID) {
0190             dev_err(&adev->dev, "Invalid coppid rxed %d\n",
0191                 open->copp_id);
0192             copp->result.status = ADSP_EBADPARAM;
0193             wake_up(&copp->wait);
0194             kref_put(&copp->refcount, q6adm_free_copp);
0195             break;
0196         }
0197         copp->result.opcode = hdr->opcode;
0198         copp->id = open->copp_id;
0199         wake_up(&copp->wait);
0200         kref_put(&copp->refcount, q6adm_free_copp);
0201     }
0202     break;
0203     default:
0204         dev_err(&adev->dev, "Unknown cmd:0x%x\n",
0205                hdr->opcode);
0206         break;
0207     }
0208 
0209     return 0;
0210 }
0211 
0212 static struct q6copp *q6adm_alloc_copp(struct q6adm *adm, int port_idx)
0213 {
0214     struct q6copp *c;
0215     int idx;
0216 
0217     idx = find_first_zero_bit(&adm->copp_bitmap[port_idx],
0218                   MAX_COPPS_PER_PORT);
0219 
0220     if (idx >= MAX_COPPS_PER_PORT)
0221         return ERR_PTR(-EBUSY);
0222 
0223     c = kzalloc(sizeof(*c), GFP_ATOMIC);
0224     if (!c)
0225         return ERR_PTR(-ENOMEM);
0226 
0227     set_bit(idx, &adm->copp_bitmap[port_idx]);
0228     c->copp_idx = idx;
0229     c->afe_port = port_idx;
0230     c->adm = adm;
0231 
0232     init_waitqueue_head(&c->wait);
0233 
0234     return c;
0235 }
0236 
0237 static int q6adm_apr_send_copp_pkt(struct q6adm *adm, struct q6copp *copp,
0238                    struct apr_pkt *pkt, uint32_t rsp_opcode)
0239 {
0240     struct device *dev = adm->dev;
0241     uint32_t opcode = pkt->hdr.opcode;
0242     int ret;
0243 
0244     mutex_lock(&adm->lock);
0245     copp->result.opcode = 0;
0246     copp->result.status = 0;
0247     ret = apr_send_pkt(adm->apr, pkt);
0248     if (ret < 0) {
0249         dev_err(dev, "Failed to send APR packet\n");
0250         ret = -EINVAL;
0251         goto err;
0252     }
0253 
0254     /* Wait for the callback with copp id */
0255     if (rsp_opcode)
0256         ret = wait_event_timeout(copp->wait,
0257                      (copp->result.opcode == opcode) ||
0258                      (copp->result.opcode == rsp_opcode),
0259                      msecs_to_jiffies(TIMEOUT_MS));
0260     else
0261         ret = wait_event_timeout(copp->wait,
0262                      (copp->result.opcode == opcode),
0263                      msecs_to_jiffies(TIMEOUT_MS));
0264 
0265     if (!ret) {
0266         dev_err(dev, "ADM copp cmd timedout\n");
0267         ret = -ETIMEDOUT;
0268     } else if (copp->result.status > 0) {
0269         dev_err(dev, "DSP returned error[%d]\n",
0270             copp->result.status);
0271         ret = -EINVAL;
0272     }
0273 
0274 err:
0275     mutex_unlock(&adm->lock);
0276     return ret;
0277 }
0278 
0279 static int q6adm_device_close(struct q6adm *adm, struct q6copp *copp,
0280                   int port_id, int copp_idx)
0281 {
0282     struct apr_pkt close;
0283 
0284     close.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
0285                     APR_HDR_LEN(APR_HDR_SIZE),
0286                     APR_PKT_VER);
0287     close.hdr.pkt_size = sizeof(close);
0288     close.hdr.src_port = port_id;
0289     close.hdr.dest_port = copp->id;
0290     close.hdr.token = port_id << 16 | copp_idx;
0291     close.hdr.opcode = ADM_CMD_DEVICE_CLOSE_V5;
0292 
0293     return q6adm_apr_send_copp_pkt(adm, copp, &close, 0);
0294 }
0295 
0296 static struct q6copp *q6adm_find_matching_copp(struct q6adm *adm,
0297                            int port_id, int topology,
0298                            int mode, int rate,
0299                            int channel_mode, int bit_width,
0300                            int app_type)
0301 {
0302     struct q6copp *c;
0303     struct q6copp *ret = NULL;
0304     unsigned long flags;
0305 
0306     spin_lock_irqsave(&adm->copps_list_lock, flags);
0307 
0308     list_for_each_entry(c, &adm->copps_list, node) {
0309         if ((port_id == c->afe_port) && (topology == c->topology) &&
0310             (mode == c->mode) && (rate == c->rate) &&
0311             (bit_width == c->bit_width) && (app_type == c->app_type)) {
0312             ret = c;
0313             kref_get(&c->refcount);
0314         }
0315     }
0316     spin_unlock_irqrestore(&adm->copps_list_lock, flags);
0317 
0318     return ret;
0319 }
0320 
0321 static int q6adm_device_open(struct q6adm *adm, struct q6copp *copp,
0322                  int port_id, int path, int topology,
0323                  int channel_mode, int bit_width, int rate)
0324 {
0325     struct q6adm_cmd_device_open_v5 *open;
0326     int afe_port = q6afe_get_port_id(port_id);
0327     struct apr_pkt *pkt;
0328     void *p;
0329     int ret, pkt_size;
0330 
0331     pkt_size = APR_HDR_SIZE + sizeof(*open);
0332     p = kzalloc(pkt_size, GFP_KERNEL);
0333     if (!p)
0334         return -ENOMEM;
0335 
0336     pkt = p;
0337     open = p + APR_HDR_SIZE;
0338     pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
0339                        APR_HDR_LEN(APR_HDR_SIZE),
0340                        APR_PKT_VER);
0341     pkt->hdr.pkt_size = pkt_size;
0342     pkt->hdr.src_port = afe_port;
0343     pkt->hdr.dest_port = afe_port;
0344     pkt->hdr.token = port_id << 16 | copp->copp_idx;
0345     pkt->hdr.opcode = ADM_CMD_DEVICE_OPEN_V5;
0346     open->flags = ADM_LEGACY_DEVICE_SESSION;
0347     open->mode_of_operation = path;
0348     open->endpoint_id_1 = afe_port;
0349     open->topology_id = topology;
0350     open->dev_num_channel = channel_mode & 0x00FF;
0351     open->bit_width = bit_width;
0352     open->sample_rate = rate;
0353 
0354     ret = q6dsp_map_channels(&open->dev_channel_mapping[0],
0355                  channel_mode);
0356     if (ret)
0357         goto err;
0358 
0359     ret = q6adm_apr_send_copp_pkt(adm, copp, pkt,
0360                       ADM_CMDRSP_DEVICE_OPEN_V5);
0361 
0362 err:
0363     kfree(pkt);
0364     return ret;
0365 }
0366 
0367 /**
0368  * q6adm_open() - open adm and grab a free copp
0369  *
0370  * @dev: Pointer to adm child device.
0371  * @port_id: port id
0372  * @path: playback or capture path.
0373  * @rate: rate at which copp is required.
0374  * @channel_mode: channel mode
0375  * @topology: adm topology id
0376  * @perf_mode: performace mode.
0377  * @bit_width: audio sample bit width
0378  * @app_type: Application type.
0379  * @acdb_id: ACDB id
0380  *
0381  * Return: Will be an negative on error or a valid copp pointer on success.
0382  */
0383 struct q6copp *q6adm_open(struct device *dev, int port_id, int path, int rate,
0384            int channel_mode, int topology, int perf_mode,
0385            uint16_t bit_width, int app_type, int acdb_id)
0386 {
0387     struct q6adm *adm = dev_get_drvdata(dev->parent);
0388     struct q6copp *copp;
0389     unsigned long flags;
0390     int ret = 0;
0391 
0392     if (port_id < 0) {
0393         dev_err(dev, "Invalid port_id %d\n", port_id);
0394         return ERR_PTR(-EINVAL);
0395     }
0396 
0397     copp = q6adm_find_matching_copp(adm, port_id, topology, perf_mode,
0398                       rate, channel_mode, bit_width, app_type);
0399     if (copp) {
0400         dev_err(dev, "Found Matching Copp 0x%x\n", copp->copp_idx);
0401         return copp;
0402     }
0403 
0404     spin_lock_irqsave(&adm->copps_list_lock, flags);
0405     copp = q6adm_alloc_copp(adm, port_id);
0406     if (IS_ERR(copp)) {
0407         spin_unlock_irqrestore(&adm->copps_list_lock, flags);
0408         return ERR_CAST(copp);
0409     }
0410 
0411     list_add_tail(&copp->node, &adm->copps_list);
0412     spin_unlock_irqrestore(&adm->copps_list_lock, flags);
0413 
0414     kref_init(&copp->refcount);
0415     copp->topology = topology;
0416     copp->mode = perf_mode;
0417     copp->rate = rate;
0418     copp->channels = channel_mode;
0419     copp->bit_width = bit_width;
0420     copp->app_type = app_type;
0421 
0422     ret = q6adm_device_open(adm, copp, port_id, path, topology,
0423                 channel_mode, bit_width, rate);
0424     if (ret < 0) {
0425         kref_put(&copp->refcount, q6adm_free_copp);
0426         return ERR_PTR(ret);
0427     }
0428 
0429     return copp;
0430 }
0431 EXPORT_SYMBOL_GPL(q6adm_open);
0432 
0433 /**
0434  * q6adm_get_copp_id() - get copp index
0435  *
0436  * @copp: Pointer to valid copp
0437  *
0438  * Return: Will be an negative on error or a valid copp index on success.
0439  **/
0440 int q6adm_get_copp_id(struct q6copp *copp)
0441 {
0442     if (!copp)
0443         return -EINVAL;
0444 
0445     return copp->copp_idx;
0446 }
0447 EXPORT_SYMBOL_GPL(q6adm_get_copp_id);
0448 
0449 /**
0450  * q6adm_matrix_map() - Map asm streams and afe ports using payload
0451  *
0452  * @dev: Pointer to adm child device.
0453  * @path: playback or capture path.
0454  * @payload_map: map between session id and afe ports.
0455  * @perf_mode: Performace mode.
0456  *
0457  * Return: Will be an negative on error or a zero on success.
0458  */
0459 int q6adm_matrix_map(struct device *dev, int path,
0460              struct route_payload payload_map, int perf_mode)
0461 {
0462     struct q6adm *adm = dev_get_drvdata(dev->parent);
0463     struct q6adm_cmd_matrix_map_routings_v5 *route;
0464     struct q6adm_session_map_node_v5 *node;
0465     struct apr_pkt *pkt;
0466     uint16_t *copps_list;
0467     int pkt_size, ret, i, copp_idx;
0468     void *matrix_map;
0469     struct q6copp *copp;
0470 
0471     /* Assumes port_ids have already been validated during adm_open */
0472     pkt_size = (APR_HDR_SIZE + sizeof(*route) +  sizeof(*node) +
0473             (sizeof(uint32_t) * payload_map.num_copps));
0474 
0475     matrix_map = kzalloc(pkt_size, GFP_KERNEL);
0476     if (!matrix_map)
0477         return -ENOMEM;
0478 
0479     pkt = matrix_map;
0480     route = matrix_map + APR_HDR_SIZE;
0481     node = matrix_map + APR_HDR_SIZE + sizeof(*route);
0482     copps_list = matrix_map + APR_HDR_SIZE + sizeof(*route) + sizeof(*node);
0483 
0484     pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
0485                        APR_HDR_LEN(APR_HDR_SIZE),
0486                        APR_PKT_VER);
0487     pkt->hdr.pkt_size = pkt_size;
0488     pkt->hdr.token = 0;
0489     pkt->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5;
0490     route->num_sessions = 1;
0491 
0492     switch (path) {
0493     case ADM_PATH_PLAYBACK:
0494         route->matrix_id = ADM_MATRIX_ID_AUDIO_RX;
0495         break;
0496     case ADM_PATH_LIVE_REC:
0497         route->matrix_id = ADM_MATRIX_ID_AUDIO_TX;
0498         break;
0499     default:
0500         dev_err(dev, "Wrong path set[%d]\n", path);
0501         break;
0502     }
0503 
0504     node->session_id = payload_map.session_id;
0505     node->num_copps = payload_map.num_copps;
0506 
0507     for (i = 0; i < payload_map.num_copps; i++) {
0508         int port_idx = payload_map.port_id[i];
0509 
0510         if (port_idx < 0) {
0511             dev_err(dev, "Invalid port_id %d\n",
0512                 payload_map.port_id[i]);
0513             kfree(pkt);
0514             return -EINVAL;
0515         }
0516         copp_idx = payload_map.copp_idx[i];
0517 
0518         copp = q6adm_find_copp(adm, port_idx, copp_idx);
0519         if (!copp) {
0520             kfree(pkt);
0521             return -EINVAL;
0522         }
0523 
0524         copps_list[i] = copp->id;
0525         kref_put(&copp->refcount, q6adm_free_copp);
0526     }
0527 
0528     mutex_lock(&adm->lock);
0529     adm->result.status = 0;
0530     adm->result.opcode = 0;
0531 
0532     ret = apr_send_pkt(adm->apr, pkt);
0533     if (ret < 0) {
0534         dev_err(dev, "routing for stream %d failed ret %d\n",
0535                payload_map.session_id, ret);
0536         goto fail_cmd;
0537     }
0538     ret = wait_event_timeout(adm->matrix_map_wait,
0539                  adm->result.opcode == pkt->hdr.opcode,
0540                  msecs_to_jiffies(TIMEOUT_MS));
0541     if (!ret) {
0542         dev_err(dev, "routing for stream %d failed\n",
0543                payload_map.session_id);
0544         ret = -ETIMEDOUT;
0545         goto fail_cmd;
0546     } else if (adm->result.status > 0) {
0547         dev_err(dev, "DSP returned error[%d]\n",
0548             adm->result.status);
0549         ret = -EINVAL;
0550         goto fail_cmd;
0551     }
0552 
0553 fail_cmd:
0554     mutex_unlock(&adm->lock);
0555     kfree(pkt);
0556     return ret;
0557 }
0558 EXPORT_SYMBOL_GPL(q6adm_matrix_map);
0559 
0560 /**
0561  * q6adm_close() - Close adm copp
0562  *
0563  * @dev: Pointer to adm child device.
0564  * @copp: pointer to previously opened copp
0565  *
0566  * Return: Will be an negative on error or a zero on success.
0567  */
0568 int q6adm_close(struct device *dev, struct q6copp *copp)
0569 {
0570     struct q6adm *adm = dev_get_drvdata(dev->parent);
0571     int ret = 0;
0572 
0573     ret = q6adm_device_close(adm, copp, copp->afe_port, copp->copp_idx);
0574     if (ret < 0) {
0575         dev_err(adm->dev, "Failed to close copp %d\n", ret);
0576         return ret;
0577     }
0578 
0579     kref_put(&copp->refcount, q6adm_free_copp);
0580 
0581     return 0;
0582 }
0583 EXPORT_SYMBOL_GPL(q6adm_close);
0584 
0585 static int q6adm_probe(struct apr_device *adev)
0586 {
0587     struct device *dev = &adev->dev;
0588     struct q6adm *adm;
0589 
0590     adm = devm_kzalloc(dev, sizeof(*adm), GFP_KERNEL);
0591     if (!adm)
0592         return -ENOMEM;
0593 
0594     adm->apr = adev;
0595     dev_set_drvdata(dev, adm);
0596     adm->dev = dev;
0597     q6core_get_svc_api_info(adev->svc_id, &adm->ainfo);
0598     mutex_init(&adm->lock);
0599     init_waitqueue_head(&adm->matrix_map_wait);
0600 
0601     INIT_LIST_HEAD(&adm->copps_list);
0602     spin_lock_init(&adm->copps_list_lock);
0603 
0604     return devm_of_platform_populate(dev);
0605 }
0606 
0607 #ifdef CONFIG_OF
0608 static const struct of_device_id q6adm_device_id[]  = {
0609     { .compatible = "qcom,q6adm" },
0610     {},
0611 };
0612 MODULE_DEVICE_TABLE(of, q6adm_device_id);
0613 #endif
0614 
0615 static struct apr_driver qcom_q6adm_driver = {
0616     .probe = q6adm_probe,
0617     .callback = q6adm_callback,
0618     .driver = {
0619         .name = "qcom-q6adm",
0620         .of_match_table = of_match_ptr(q6adm_device_id),
0621     },
0622 };
0623 
0624 module_apr_driver(qcom_q6adm_driver);
0625 MODULE_DESCRIPTION("Q6 Audio Device Manager");
0626 MODULE_LICENSE("GPL v2");