linux/drivers/staging/dream/qdsp5/audpp.c
<<
>>
Prefs
   1
   2/* arch/arm/mach-msm/qdsp5/audpp.c
   3 *
   4 * common code to deal with the AUDPP dsp task (audio postproc)
   5 *
   6 * Copyright (C) 2008 Google, Inc.
   7 *
   8 * This software is licensed under the terms of the GNU General Public
   9 * License version 2, as published by the Free Software Foundation, and
  10 * may be copied, distributed, and modified under those terms.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 */
  18
  19#include <linux/kernel.h>
  20#include <linux/module.h>
  21#include <linux/wait.h>
  22#include <linux/delay.h>
  23
  24#include <asm/atomic.h>
  25#include <asm/ioctls.h>
  26#include <mach/msm_adsp.h>
  27
  28#include "audmgr.h"
  29
  30#include <mach/qdsp5/qdsp5audppcmdi.h>
  31#include <mach/qdsp5/qdsp5audppmsg.h>
  32
  33/* for queue ids - should be relative to module number*/
  34#include "adsp.h"
  35
  36#include "evlog.h"
  37
  38
  39enum {
  40        EV_NULL,
  41        EV_ENABLE,
  42        EV_DISABLE,
  43        EV_EVENT,
  44        EV_DATA,
  45};
  46
  47static const char *dsp_log_strings[] = {
  48        "NULL",
  49        "ENABLE",
  50        "DISABLE",
  51        "EVENT",
  52        "DATA",
  53};
  54
  55DECLARE_LOG(dsp_log, 64, dsp_log_strings);
  56
  57static int __init _dsp_log_init(void)
  58{
  59        return ev_log_init(&dsp_log);
  60}
  61module_init(_dsp_log_init);
  62#define LOG(id,arg) ev_log_write(&dsp_log, id, arg)
  63
  64static DEFINE_MUTEX(audpp_lock);
  65
  66#define CH_COUNT 5
  67#define AUDPP_CLNT_MAX_COUNT 6
  68#define AUDPP_AVSYNC_INFO_SIZE 7
  69
  70struct audpp_state {
  71        struct msm_adsp_module *mod;
  72        audpp_event_func func[AUDPP_CLNT_MAX_COUNT];
  73        void *private[AUDPP_CLNT_MAX_COUNT];
  74        struct mutex *lock;
  75        unsigned open_count;
  76        unsigned enabled;
  77
  78        /* which channels are actually enabled */
  79        unsigned avsync_mask;
  80
  81        /* flags, 48 bits sample/bytes counter per channel */
  82        uint16_t avsync[CH_COUNT * AUDPP_CLNT_MAX_COUNT + 1];
  83};
  84
  85struct audpp_state the_audpp_state = {
  86        .lock = &audpp_lock,
  87};
  88
  89int audpp_send_queue1(void *cmd, unsigned len)
  90{
  91        return msm_adsp_write(the_audpp_state.mod,
  92                              QDSP_uPAudPPCmd1Queue, cmd, len);
  93}
  94EXPORT_SYMBOL(audpp_send_queue1);
  95
  96int audpp_send_queue2(void *cmd, unsigned len)
  97{
  98        return msm_adsp_write(the_audpp_state.mod,
  99                              QDSP_uPAudPPCmd2Queue, cmd, len);
 100}
 101EXPORT_SYMBOL(audpp_send_queue2);
 102
 103int audpp_send_queue3(void *cmd, unsigned len)
 104{
 105        return msm_adsp_write(the_audpp_state.mod,
 106                              QDSP_uPAudPPCmd3Queue, cmd, len);
 107}
 108EXPORT_SYMBOL(audpp_send_queue3);
 109
 110static int audpp_dsp_config(int enable)
 111{
 112        audpp_cmd_cfg cmd;
 113
 114        cmd.cmd_id = AUDPP_CMD_CFG;
 115        cmd.cfg = enable ? AUDPP_CMD_CFG_ENABLE : AUDPP_CMD_CFG_SLEEP;
 116
 117        return audpp_send_queue1(&cmd, sizeof(cmd));
 118}
 119
 120static void audpp_broadcast(struct audpp_state *audpp, unsigned id,
 121                            uint16_t *msg)
 122{
 123        unsigned n;
 124        for (n = 0; n < AUDPP_CLNT_MAX_COUNT; n++) {
 125                if (audpp->func[n])
 126                        audpp->func[n] (audpp->private[n], id, msg);
 127        }
 128}
 129
 130static void audpp_notify_clnt(struct audpp_state *audpp, unsigned clnt_id,
 131                              unsigned id, uint16_t *msg)
 132{
 133        if (clnt_id < AUDPP_CLNT_MAX_COUNT && audpp->func[clnt_id])
 134                audpp->func[clnt_id] (audpp->private[clnt_id], id, msg);
 135}
 136
 137static void audpp_dsp_event(void *data, unsigned id, size_t len,
 138                            void (*getevent)(void *ptr, size_t len))
 139{
 140        struct audpp_state *audpp = data;
 141        uint16_t msg[8];
 142
 143        if (id == AUDPP_MSG_AVSYNC_MSG) {
 144                getevent(audpp->avsync, sizeof(audpp->avsync));
 145
 146                /* mask off any channels we're not watching to avoid
 147                 * cases where we might get one last update after
 148                 * disabling avsync and end up in an odd state when
 149                 * we next read...
 150                 */
 151                audpp->avsync[0] &= audpp->avsync_mask;
 152                return;
 153        }
 154
 155        getevent(msg, sizeof(msg));
 156
 157        LOG(EV_EVENT, (id << 16) | msg[0]);
 158        LOG(EV_DATA, (msg[1] << 16) | msg[2]);
 159
 160        switch (id) {
 161        case AUDPP_MSG_STATUS_MSG:{
 162                        unsigned cid = msg[0];
 163                        pr_info("audpp: status %d %d %d\n", cid, msg[1],
 164                                msg[2]);
 165                        if ((cid < 5) && audpp->func[cid])
 166                                audpp->func[cid] (audpp->private[cid], id, msg);
 167                        break;
 168                }
 169        case AUDPP_MSG_HOST_PCM_INTF_MSG:
 170                if (audpp->func[5])
 171                        audpp->func[5] (audpp->private[5], id, msg);
 172                break;
 173        case AUDPP_MSG_PCMDMAMISSED:
 174                pr_err("audpp: DMA missed obj=%x\n", msg[0]);
 175                break;
 176        case AUDPP_MSG_CFG_MSG:
 177                if (msg[0] == AUDPP_MSG_ENA_ENA) {
 178                        pr_info("audpp: ENABLE\n");
 179                        audpp->enabled = 1;
 180                        audpp_broadcast(audpp, id, msg);
 181                } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
 182                        pr_info("audpp: DISABLE\n");
 183                        audpp->enabled = 0;
 184                        audpp_broadcast(audpp, id, msg);
 185                } else {
 186                        pr_err("audpp: invalid config msg %d\n", msg[0]);
 187                }
 188                break;
 189        case AUDPP_MSG_ROUTING_ACK:
 190                audpp_broadcast(audpp, id, msg);
 191                break;
 192        case AUDPP_MSG_FLUSH_ACK:
 193                audpp_notify_clnt(audpp, msg[0], id, msg);
 194                break;
 195        default:
 196          pr_info("audpp: unhandled msg id %x\n", id);
 197        }
 198}
 199
 200static struct msm_adsp_ops adsp_ops = {
 201        .event = audpp_dsp_event,
 202};
 203
 204static void audpp_fake_event(struct audpp_state *audpp, int id,
 205                             unsigned event, unsigned arg)
 206{
 207        uint16_t msg[1];
 208        msg[0] = arg;
 209        audpp->func[id] (audpp->private[id], event, msg);
 210}
 211
 212int audpp_enable(int id, audpp_event_func func, void *private)
 213{
 214        struct audpp_state *audpp = &the_audpp_state;
 215        int res = 0;
 216
 217        if (id < -1 || id > 4)
 218                return -EINVAL;
 219
 220        if (id == -1)
 221                id = 5;
 222
 223        mutex_lock(audpp->lock);
 224        if (audpp->func[id]) {
 225                res = -EBUSY;
 226                goto out;
 227        }
 228
 229        audpp->func[id] = func;
 230        audpp->private[id] = private;
 231
 232        LOG(EV_ENABLE, 1);
 233        if (audpp->open_count++ == 0) {
 234                pr_info("audpp: enable\n");
 235                res = msm_adsp_get("AUDPPTASK", &audpp->mod, &adsp_ops, audpp);
 236                if (res < 0) {
 237                        pr_err("audpp: cannot open AUDPPTASK\n");
 238                        audpp->open_count = 0;
 239                        audpp->func[id] = NULL;
 240                        audpp->private[id] = NULL;
 241                        goto out;
 242                }
 243                LOG(EV_ENABLE, 2);
 244                msm_adsp_enable(audpp->mod);
 245                audpp_dsp_config(1);
 246        } else {
 247                unsigned long flags;
 248                local_irq_save(flags);
 249                if (audpp->enabled)
 250                        audpp_fake_event(audpp, id,
 251                                         AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_ENA);
 252                local_irq_restore(flags);
 253        }
 254
 255        res = 0;
 256out:
 257        mutex_unlock(audpp->lock);
 258        return res;
 259}
 260EXPORT_SYMBOL(audpp_enable);
 261
 262void audpp_disable(int id, void *private)
 263{
 264        struct audpp_state *audpp = &the_audpp_state;
 265        unsigned long flags;
 266
 267        if (id < -1 || id > 4)
 268                return;
 269
 270        if (id == -1)
 271                id = 5;
 272
 273        mutex_lock(audpp->lock);
 274        LOG(EV_DISABLE, 1);
 275        if (!audpp->func[id])
 276                goto out;
 277        if (audpp->private[id] != private)
 278                goto out;
 279
 280        local_irq_save(flags);
 281        audpp_fake_event(audpp, id, AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_DIS);
 282        audpp->func[id] = NULL;
 283        audpp->private[id] = NULL;
 284        local_irq_restore(flags);
 285
 286        if (--audpp->open_count == 0) {
 287                pr_info("audpp: disable\n");
 288                LOG(EV_DISABLE, 2);
 289                audpp_dsp_config(0);
 290                msm_adsp_disable(audpp->mod);
 291                msm_adsp_put(audpp->mod);
 292                audpp->mod = NULL;
 293        }
 294out:
 295        mutex_unlock(audpp->lock);
 296}
 297EXPORT_SYMBOL(audpp_disable);
 298
 299#define BAD_ID(id) ((id < 0) || (id >= CH_COUNT))
 300
 301void audpp_avsync(int id, unsigned rate)
 302{
 303        unsigned long flags;
 304        audpp_cmd_avsync cmd;
 305
 306        if (BAD_ID(id))
 307                return;
 308
 309        local_irq_save(flags);
 310        if (rate)
 311                the_audpp_state.avsync_mask |= (1 << id);
 312        else
 313                the_audpp_state.avsync_mask &= (~(1 << id));
 314        the_audpp_state.avsync[0] &= the_audpp_state.avsync_mask;
 315        local_irq_restore(flags);
 316
 317        cmd.cmd_id = AUDPP_CMD_AVSYNC;
 318        cmd.object_number = id;
 319        cmd.interrupt_interval_lsw = rate;
 320        cmd.interrupt_interval_msw = rate >> 16;
 321        audpp_send_queue1(&cmd, sizeof(cmd));
 322}
 323EXPORT_SYMBOL(audpp_avsync);
 324
 325unsigned audpp_avsync_sample_count(int id)
 326{
 327        uint16_t *avsync = the_audpp_state.avsync;
 328        unsigned val;
 329        unsigned long flags;
 330        unsigned mask;
 331
 332        if (BAD_ID(id))
 333                return 0;
 334
 335        mask = 1 << id;
 336        id = id * AUDPP_AVSYNC_INFO_SIZE + 2;
 337        local_irq_save(flags);
 338        if (avsync[0] & mask)
 339                val = (avsync[id] << 16) | avsync[id + 1];
 340        else
 341                val = 0;
 342        local_irq_restore(flags);
 343
 344        return val;
 345}
 346EXPORT_SYMBOL(audpp_avsync_sample_count);
 347
 348unsigned audpp_avsync_byte_count(int id)
 349{
 350        uint16_t *avsync = the_audpp_state.avsync;
 351        unsigned val;
 352        unsigned long flags;
 353        unsigned mask;
 354
 355        if (BAD_ID(id))
 356                return 0;
 357
 358        mask = 1 << id;
 359        id = id * AUDPP_AVSYNC_INFO_SIZE + 5;
 360        local_irq_save(flags);
 361        if (avsync[0] & mask)
 362                val = (avsync[id] << 16) | avsync[id + 1];
 363        else
 364                val = 0;
 365        local_irq_restore(flags);
 366
 367        return val;
 368}
 369EXPORT_SYMBOL(audpp_avsync_byte_count);
 370
 371#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000
 372#define AUDPP_CMD_VOLUME_PAN 0
 373
 374int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan)
 375{
 376        /* cmd, obj_cfg[7], cmd_type, volume, pan */
 377        uint16_t cmd[11];
 378
 379        if (id > 6)
 380                return -EINVAL;
 381
 382        memset(cmd, 0, sizeof(cmd));
 383        cmd[0] = AUDPP_CMD_CFG_OBJECT_PARAMS;
 384        cmd[1 + id] = AUDPP_CMD_CFG_OBJ_UPDATE;
 385        cmd[8] = AUDPP_CMD_VOLUME_PAN;
 386        cmd[9] = volume;
 387        cmd[10] = pan;
 388
 389        return audpp_send_queue3(cmd, sizeof(cmd));
 390}
 391EXPORT_SYMBOL(audpp_set_volume_and_pan);
 392
 393int audpp_pause(unsigned id, int pause)
 394{
 395        /* pause 1 = pause 0 = resume */
 396        u16 pause_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)];
 397
 398        if (id >= CH_COUNT)
 399                return -EINVAL;
 400
 401        memset(pause_cmd, 0, sizeof(pause_cmd));
 402
 403        pause_cmd[0] = AUDPP_CMD_DEC_CTRL;
 404        if (pause == 1)
 405                pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_PAUSE_V;
 406        else if (pause == 0)
 407                pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_RESUME_V;
 408        else
 409                return -EINVAL;
 410
 411        return audpp_send_queue1(pause_cmd, sizeof(pause_cmd));
 412}
 413EXPORT_SYMBOL(audpp_pause);
 414
 415int audpp_flush(unsigned id)
 416{
 417        u16 flush_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)];
 418
 419        if (id >= CH_COUNT)
 420                return -EINVAL;
 421
 422        memset(flush_cmd, 0, sizeof(flush_cmd));
 423
 424        flush_cmd[0] = AUDPP_CMD_DEC_CTRL;
 425        flush_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_FLUSH_V;
 426
 427        return audpp_send_queue1(flush_cmd, sizeof(flush_cmd));
 428}
 429EXPORT_SYMBOL(audpp_flush);
 430