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