diff --git a/sound/soc/fsl/fsl_hifi4.c b/sound/soc/fsl/fsl_hifi4.c index 7a113f5d7128d9c2d9a181916906f8a3c251c94c..37d3292f08618af0d970b1598b3b4af879c7f5de 100644 --- a/sound/soc/fsl/fsl_hifi4.c +++ b/sound/soc/fsl/fsl_hifi4.c @@ -60,11 +60,13 @@ struct decode_info_compat32 { __s32 out_buf_off; __u32 cycles; __u32 input_over; + __u32 process_id; }; struct binary_info_compat32 { __s32 type; compat_long_t file; + __u32 process_id; }; static int get_binary_info_compat32(struct binary_info *kp, @@ -75,7 +77,8 @@ static int get_binary_info_compat32(struct binary_info *kp, if (!access_ok(VERIFY_READ, up, sizeof(struct binary_info_compat32)) || get_user(kp->type, &up->type) || - get_user(p, &up->file) + get_user(p, &up->file) || + get_user(kp->process_id, &up->process_id) ) { return -EFAULT; } @@ -86,6 +89,19 @@ static int get_binary_info_compat32(struct binary_info *kp, return 0; } +static int put_binary_info_compat32(struct binary_info *kp, + struct binary_info_compat32 *up) +{ + + if (!access_ok(VERIFY_WRITE, up, sizeof(struct binary_info_compat32)) || + put_user(kp->process_id, &up->process_id) + ) { + return -EFAULT; + } + + return 0; +} + static int get_decode_info_compat32(struct decode_info *kp, struct decode_info_compat32 *up) { @@ -102,7 +118,8 @@ static int get_decode_info_compat32(struct decode_info *kp, get_user(kp->out_buf_size, &up->out_buf_size) || get_user(kp->out_buf_off, &up->out_buf_off) || get_user(kp->cycles, &up->cycles) || - get_user(kp->input_over, &up->input_over) + get_user(kp->input_over, &up->input_over) || + get_user(kp->process_id, &up->process_id) ) { return -EFAULT; } @@ -133,6 +150,66 @@ static int put_decode_info_compat32(struct decode_info *kp, } #endif +long switch_codec(struct fsl_hifi4 *hifi4_priv, int id) +{ + union icm_header_t apu_icm; + struct hifi4_ext_msg ext_msg; + struct icm_switch_info_t switch_info; + int i; + long ret = 0; + + switch_info.proc_id = id; + switch_info.status = 0; + + init_completion(&hifi4_priv->cmd_complete); + hifi4_priv->is_done = 0; + + apu_icm.allbits = 0; /* clear all bits; */ + + apu_icm.ack = 0; + apu_icm.intr = 1; + apu_icm.msg = ICM_SWITCH_CODEC; + apu_icm.size = 8; + + ext_msg.phys = hifi4_priv->msg_buf_phys; + ext_msg.size = sizeof(struct icm_switch_info_t); + memcpy(hifi4_priv->msg_buf_virt, &switch_info, + sizeof(struct icm_switch_info_t)); + + icm_intr_extended_send(hifi4_priv, apu_icm.allbits, &ext_msg); + + /* wait for response here */ + ret = icm_ack_wait(hifi4_priv, apu_icm.allbits); + if (ret) + return ret; + + /* check whether the dsp framework switches successfully or not */ + ret = hifi4_priv->ret_status; + if (ret) + return ret; + + /* Because this variables are shared for every codec, so when + * switching, need to recover it value for current codec. + */ + for (i = 0; i < MULTI_CODEC_NUM; i++) { + if (hifi4_priv->process_info[i].process_id == id) { + if (hifi4_priv->process_info[i].status) { + hifi4_priv->cur_res_id = i; + hifi4_priv->pil_info = + hifi4_priv->process_info[i].pil_info_info; + hifi4_priv->objtype = + hifi4_priv->process_info[i].codec_id; + hifi4_priv->codec_iobuf_info.proc_id = + hifi4_priv->process_info[i].proc_id; + break; + } + } + } + /* update the current process id to the new process id */ + hifi4_priv->process_id = id; + + return ret; +} long load_dpu_with_library(struct fsl_hifi4 *hifi4_priv) { @@ -140,8 +217,11 @@ long load_dpu_with_library(struct fsl_hifi4 *hifi4_priv) unsigned char *srambuf = NULL; struct lib_dnld_info_t dpulib; int filesize = 0; + unsigned int id; long ret_val = 0; + id = hifi4_priv->cur_res_id; + /* Load DPU's main program to System memory */ fpInfile = file_open_name(hifi4_priv->objfile, O_RDONLY, 0); if (IS_ERR(fpInfile)) @@ -164,8 +244,12 @@ long load_dpu_with_library(struct fsl_hifi4 *hifi4_priv) if (ret_val != XTLIB_NO_ERR) return ret_val; + hifi4_priv->size_code = dpulib.size_code; + hifi4_priv->size_data = dpulib.size_data; + dpulib.pbuf_code = (unsigned long)hifi4_priv->code_buf_phys; - dpulib.pbuf_data = (unsigned long)hifi4_priv->data_buf_phys; + dpulib.pbuf_data = + (unsigned long)hifi4_priv->process_info[id].data_buf_phys; dpulib.ppil_inf = &hifi4_priv->pil_info; xtlib_host_load_split_pi_library( @@ -537,13 +621,14 @@ static xt_ptr xtlib_load_split_pi_library_common( struct xtlib_loader_globals *xtlib_globals = &hifi4_priv->xtlib_globals; Elf32_Ehdr *header = (Elf32_Ehdr *) library; Elf32_Phdr *pheader; - unsigned int align; + unsigned int align, id; int err = validate_dynamic_splitload(header, hifi4_priv); if (err != XTLIB_NO_ERR) { xtlib_globals->err = err; return 0; } + id = hifi4_priv->cur_res_id; align = find_align(header, hifi4_priv); @@ -585,7 +670,7 @@ static xt_ptr xtlib_load_split_pi_library_common( xtlib_load_seg(&pheader[1], (char *)library + xtlib_host_word(pheader[1].p_offset, xtlib_globals->byteswap), - (xt_ptr)hifi4_priv->data_buf_virt + + (xt_ptr)hifi4_priv->process_info[id].data_buf_virt + xtlib_host_word(pheader[1].p_paddr, xtlib_globals->byteswap), mcpy_fn, mset_fn, hifi4_priv); @@ -618,36 +703,28 @@ xt_ptr xtlib_host_load_split_pi_library(struct xtlib_packaged_library *library, } -static long fsl_hifi4_init_codec(struct fsl_hifi4 *hifi4_priv) +static long fsl_hifi4_init_codec(struct fsl_hifi4 *hifi4_priv, + void __user *user) { + struct device *dev = hifi4_priv->dev; union icm_header_t apu_icm; struct hifi4_ext_msg ext_msg; - struct icm_pilib_size_t lib_alloc_mem; + int id; long ret = 0; - init_completion(&hifi4_priv->cmd_complete); - hifi4_priv->is_done = 0; - - apu_icm.allbits = 0; /* clear all bits; */ - apu_icm.ack = 0; - apu_icm.intr = 1; - apu_icm.msg = ICM_PI_LIB_MEM_ALLOC; - apu_icm.size = 8; - - ext_msg.phys = hifi4_priv->msg_buf_phys; - ext_msg.size = sizeof(struct icm_pilib_size_t); - - lib_alloc_mem.codec_type = hifi4_priv->objtype; - - memcpy(hifi4_priv->msg_buf_virt, &lib_alloc_mem, - sizeof(struct icm_pilib_size_t)); - icm_intr_extended_send(hifi4_priv, apu_icm.allbits, &ext_msg); - - /* wait for response here */ - ret = icm_ack_wait(hifi4_priv, apu_icm.allbits); - if (ret) - return ret; + ret = copy_from_user(&id, user, sizeof(int)); + if (ret) { + dev_err(dev, "failed to get para from user space\n"); + return -EFAULT; + } + if (hifi4_priv->process_id != id) { + ret = switch_codec(hifi4_priv, id); + if (ret) { + dev_err(dev, "failed to switch codec in codec init\n"); + return ret; + } + } init_completion(&hifi4_priv->cmd_complete); hifi4_priv->is_done = 0; @@ -687,7 +764,15 @@ static long fsl_hifi4_decode_frame(struct fsl_hifi4 *hifi4_priv, ret = copy_from_user(&decode_info, user, sizeof(decode_info)); if (ret) { dev_err(dev, "failed to get para from user space\n"); - return ret; + return -EFAULT; + } + + if (hifi4_priv->process_id != decode_info.process_id) { + ret = switch_codec(hifi4_priv, decode_info.process_id); + if (ret) { + dev_err(dev, "failed to switch codec in codec decode frame\n"); + return ret; + } } if (decode_info.in_buf_size > INPUT_BUF_SIZE || @@ -709,6 +794,7 @@ static long fsl_hifi4_decode_frame(struct fsl_hifi4 *hifi4_priv, codec_iobuf_info->inp_addr_sysram = hifi4_priv->in_buf_phys; codec_iobuf_info->inp_buf_size_max = decode_info.in_buf_size; + codec_iobuf_info->inp_cur_offset = decode_info.in_buf_off; codec_iobuf_info->out_addr_sysram = hifi4_priv->out_buf_phys; codec_iobuf_info->out_buf_size_max = hifi4_priv->out_buf_size; @@ -749,7 +835,7 @@ static long fsl_hifi4_decode_frame(struct fsl_hifi4 *hifi4_priv, ret = copy_to_user(user, &decode_info, sizeof(decode_info)); if (ret) { dev_err(dev, "failed to send para to user space\n"); - return ret; + return -EFAULT; } ret = hifi4_priv->ret_status; @@ -774,6 +860,14 @@ static long fsl_hifi4_decode_frame_compat32(struct fsl_hifi4 *hifi4_priv, return ret; } + if (hifi4_priv->process_id != decode_info.process_id) { + ret = switch_codec(hifi4_priv, decode_info.process_id); + if (ret) { + dev_err(dev, "failed to switch codec in codec decode frame in compat32 mode\n"); + return ret; + } + } + if (decode_info.in_buf_size > INPUT_BUF_SIZE || decode_info.out_buf_size != OUTPUT_BUF_SIZE) { dev_err(dev, "param error\n"); @@ -793,6 +887,7 @@ static long fsl_hifi4_decode_frame_compat32(struct fsl_hifi4 *hifi4_priv, codec_iobuf_info->inp_addr_sysram = hifi4_priv->in_buf_phys; codec_iobuf_info->inp_buf_size_max = decode_info.in_buf_size; + codec_iobuf_info->inp_cur_offset = decode_info.in_buf_off; codec_iobuf_info->out_addr_sysram = hifi4_priv->out_buf_phys; codec_iobuf_info->out_buf_size_max = hifi4_priv->out_buf_size; @@ -831,7 +926,6 @@ static long fsl_hifi4_decode_frame_compat32(struct fsl_hifi4 *hifi4_priv, decode_info.in_buf_off = codec_iobuf_info->inp_cur_offset; decode_info.out_buf_off = codec_iobuf_info->out_cur_offset; decode_info.cycles = codec_iobuf_info->cycles; - decode_info.input_over = codec_iobuf_info->input_over; ret = put_decode_info_compat32(&decode_info, user); if (ret) { @@ -856,7 +950,15 @@ static long fsl_hifi4_get_pcm_prop(struct fsl_hifi4 *hifi4_priv, ret = copy_from_user(&prop_info, user, sizeof(prop_info)); if (ret) { dev_err(dev, "failed to get para from user space\n"); - return ret; + return -EFAULT; + } + + if (hifi4_priv->process_id != prop_info.process_id) { + ret = switch_codec(hifi4_priv, prop_info.process_id); + if (ret) { + dev_err(dev, "failed to switch codec in codec get param\n"); + return ret; + } } init_completion(&hifi4_priv->cmd_complete); @@ -883,7 +985,7 @@ static long fsl_hifi4_get_pcm_prop(struct fsl_hifi4 *hifi4_priv, ret = copy_to_user(user, &prop_info, sizeof(prop_info)); if (ret) { dev_err(dev, "failed to send para to user space\n"); - return ret; + return -EFAULT; } ret = hifi4_priv->ret_status; @@ -901,7 +1003,15 @@ static int fsl_hifi4_set_config(struct fsl_hifi4 *hifi4_priv, void __user *user) ret = copy_from_user(&prop_config, user, sizeof(prop_config)); if (ret) { dev_err(dev, "failed to get para from user space: %d\n", ret); - return ret; + return -EFAULT; + } + + if (hifi4_priv->process_id != prop_config.process_id) { + ret = switch_codec(hifi4_priv, prop_config.process_id); + if (ret) { + dev_err(dev, "failed to switch codec in codec set param\n"); + return ret; + } } init_completion(&hifi4_priv->cmd_complete); @@ -922,6 +1032,7 @@ static int fsl_hifi4_set_config(struct fsl_hifi4 *hifi4_priv, void __user *user) icm_ack_wait(hifi4_priv, apu_icm.allbits); if (ret) return ret; + ret = hifi4_priv->ret_status; return ret; @@ -932,13 +1043,17 @@ static long fsl_hifi4_load_codec(struct fsl_hifi4 *hifi4_priv, { struct device *dev = hifi4_priv->dev; struct filename *fpInfile; + union icm_header_t apu_icm; struct binary_info binary_info; + struct icm_pilib_size_t lib_alloc_mem; + struct hifi4_ext_msg ext_msg; long ret = 0; + long i; ret = copy_from_user(&binary_info, user, sizeof(binary_info)); if (ret) { dev_err(dev, "failed to get para from user space\n"); - return ret; + return -EFAULT; } fpInfile = getname(binary_info.file); @@ -948,13 +1063,75 @@ static long fsl_hifi4_load_codec(struct fsl_hifi4 *hifi4_priv, return PTR_ERR(fpInfile); } + /* check whether the dsp driver has available resource or not */ + for (i = 0; i < MULTI_CODEC_NUM; i++) { + if (!(hifi4_priv->process_info[i].status)) { + hifi4_priv->process_info[i].status = 1; + hifi4_priv->available_resource--; + break; + } + } + if (i >= MULTI_CODEC_NUM) { + dev_err(dev, "out of range of multi codec max number\n"); + return -EINVAL; + } + + /* If dsp driver has available resource, produce a new process + * for the new codec. + */ + hifi4_priv->process_id_count++; + + ret = switch_codec(hifi4_priv, hifi4_priv->process_id_count); + if (ret) { + dev_err(dev, "failed to switch codec in codec load\n"); + return ret; + } + hifi4_priv->objfile = fpInfile; hifi4_priv->objtype = binary_info.type; + hifi4_priv->cur_res_id = i; ret = load_dpu_with_library(hifi4_priv); if (ret) { dev_err(dev, "failed to load code binary, err = %ld\n", ret); + } + + init_completion(&hifi4_priv->cmd_complete); + hifi4_priv->is_done = 0; + + apu_icm.allbits = 0; /* clear all bits; */ + apu_icm.ack = 0; + apu_icm.intr = 1; + apu_icm.msg = ICM_PI_LIB_MEM_ALLOC; + apu_icm.size = 8; + + ext_msg.phys = hifi4_priv->msg_buf_phys; + ext_msg.size = sizeof(struct icm_pilib_size_t); + + lib_alloc_mem.codec_type = hifi4_priv->objtype; + lib_alloc_mem.text_size = hifi4_priv->size_code; + lib_alloc_mem.data_size = hifi4_priv->size_data; + + memcpy(hifi4_priv->msg_buf_virt, &lib_alloc_mem, + sizeof(struct icm_pilib_size_t)); + icm_intr_extended_send(hifi4_priv, apu_icm.allbits, &ext_msg); + + /* wait for response here */ + ret = icm_ack_wait(hifi4_priv, apu_icm.allbits); + if (ret) return ret; + + /* save current codec information */ + hifi4_priv->process_info[i].process_id = hifi4_priv->process_id; + hifi4_priv->process_info[i].codec_id = hifi4_priv->objtype; + hifi4_priv->process_info[i].pil_info_info = hifi4_priv->pil_info; + + /* return process id of this codec to user space */ + binary_info.process_id = hifi4_priv->process_id; + ret = copy_to_user(user, &binary_info, sizeof(struct binary_info)); + if (ret) { + dev_err(dev, "failed to send para to user space\n"); + return -EFAULT; } hifi4_priv->ret_status = 0; @@ -970,8 +1147,12 @@ static long fsl_hifi4_load_codec_compat32(struct fsl_hifi4 *hifi4_priv, { struct device *dev = hifi4_priv->dev; struct filename *fpInfile; + union icm_header_t apu_icm; struct binary_info binary_info; + struct icm_pilib_size_t lib_alloc_mem; + struct hifi4_ext_msg ext_msg; long ret = 0; + long i; ret = get_binary_info_compat32(&binary_info, user); if (ret) { @@ -986,15 +1167,78 @@ static long fsl_hifi4_load_codec_compat32(struct fsl_hifi4 *hifi4_priv, return PTR_ERR(fpInfile); } + /* check whether the dsp driver has available resource or not */ + for (i = 0; i < MULTI_CODEC_NUM; i++) { + if (!(hifi4_priv->process_info[i].status)) { + hifi4_priv->process_info[i].status = 1; + hifi4_priv->available_resource--; + break; + } + } + if (i >= MULTI_CODEC_NUM) { + dev_err(dev, "out of range of multi codec max number\n"); + return -EINVAL; + } + + /* If dsp driver has available resource, produce a new process + * for the new codec. + */ + hifi4_priv->process_id_count++; + + ret = switch_codec(hifi4_priv, hifi4_priv->process_id_count); + if (ret) { + dev_err(dev, "failed to switch codec in codec load\n"); + return ret; + } + hifi4_priv->objfile = fpInfile; hifi4_priv->objtype = binary_info.type; + hifi4_priv->cur_res_id = i; ret = load_dpu_with_library(hifi4_priv); if (ret) { dev_err(dev, "failed to load code binary, err = %ld\n", ret); return ret; } + init_completion(&hifi4_priv->cmd_complete); + hifi4_priv->is_done = 0; + + apu_icm.allbits = 0; /* clear all bits; */ + apu_icm.ack = 0; + apu_icm.intr = 1; + apu_icm.msg = ICM_PI_LIB_MEM_ALLOC; + apu_icm.size = 8; + + ext_msg.phys = hifi4_priv->msg_buf_phys; + ext_msg.size = sizeof(struct icm_pilib_size_t); + + lib_alloc_mem.codec_type = hifi4_priv->objtype; + lib_alloc_mem.text_size = hifi4_priv->size_code; + lib_alloc_mem.data_size = hifi4_priv->size_data; + + memcpy(hifi4_priv->msg_buf_virt, &lib_alloc_mem, + sizeof(struct icm_pilib_size_t)); + icm_intr_extended_send(hifi4_priv, apu_icm.allbits, &ext_msg); + + /* wait for response here */ + ret = icm_ack_wait(hifi4_priv, apu_icm.allbits); + if (ret) + return ret; + + /* save current codec information */ + hifi4_priv->process_info[i].process_id = hifi4_priv->process_id; + hifi4_priv->process_info[i].codec_id = hifi4_priv->objtype; + hifi4_priv->process_info[i].pil_info_info = hifi4_priv->pil_info; + + /* return process id of this codec to user space */ + binary_info.process_id = hifi4_priv->process_id; + ret = put_binary_info_compat32(&binary_info, user); + if (ret) { + dev_err(dev, "failed to send para to user space\n"); + return ret; + } + hifi4_priv->ret_status = 0; dev_dbg(dev, "code binary is loaded\n"); @@ -1003,17 +1247,32 @@ static long fsl_hifi4_load_codec_compat32(struct fsl_hifi4 *hifi4_priv, } #endif -static long fsl_hifi4_codec_open(struct fsl_hifi4 *hifi4_priv) +static long fsl_hifi4_codec_open(struct fsl_hifi4 *hifi4_priv, + void __user *user) { + struct device *dev = hifi4_priv->dev; union icm_header_t apu_icm; struct icm_cdc_uinp_t cdc_user_inp; struct hifi4_ext_msg ext_msg; + int id; long ret = 0; + ret = copy_from_user(&id, user, sizeof(int)); + if (ret) { + dev_err(dev, "failed to get para from user space\n"); + return -EFAULT; + } + + if (hifi4_priv->process_id != id) { + ret = switch_codec(hifi4_priv, id); + if (ret) { + dev_err(dev, "failed to switch codec in codec open\n"); + return ret; + } + } + cdc_user_inp.proc_id = 0; cdc_user_inp.codec_id = hifi4_priv->objtype; - cdc_user_inp.crc_check = 0; - cdc_user_inp.pcm_wd_sz = 16; init_completion(&hifi4_priv->cmd_complete); hifi4_priv->is_done = 0; @@ -1037,15 +1296,36 @@ static long fsl_hifi4_codec_open(struct fsl_hifi4 *hifi4_priv) if (ret) return ret; + /* save current codec information */ + hifi4_priv->process_info[hifi4_priv->cur_res_id].proc_id = + hifi4_priv->codec_iobuf_info.proc_id; + ret = hifi4_priv->ret_status; return ret; } -static int fsl_hifi4_codec_close(struct fsl_hifi4 *hifi4_priv) +static int fsl_hifi4_codec_close(struct fsl_hifi4 *hifi4_priv, + void __user *user) { + struct device *dev = hifi4_priv->dev; union icm_header_t apu_icm; + int id; long ret = 0; + ret = copy_from_user(&id, user, sizeof(int)); + if (ret) { + dev_err(dev, "failed to get para from user space\n"); + return -EFAULT; + } + + if (hifi4_priv->process_id != id) { + ret = switch_codec(hifi4_priv, id); + if (ret) { + dev_err(dev, "failed to switch codec in codec close\n"); + return ret; + } + } + init_completion(&hifi4_priv->cmd_complete); hifi4_priv->is_done = 0; @@ -1058,6 +1338,18 @@ static int fsl_hifi4_codec_close(struct fsl_hifi4 *hifi4_priv) /* wait for response here */ ret = icm_ack_wait(hifi4_priv, apu_icm.allbits); + if (ret) + return ret; + + /* Making status to 0 means releasing the resource that + * current codec occupies. + */ + hifi4_priv->process_info[hifi4_priv->cur_res_id].status = 0; + hifi4_priv->available_resource++; + + /* If no codec occupies the resource, zero the process id count */ + if (hifi4_priv->available_resource >= MULTI_CODEC_NUM) + hifi4_priv->process_id_count = 0; return ret; } @@ -1070,26 +1362,32 @@ static struct miscdevice hifi4_miscdev = { static long fsl_hifi4_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct fsl_hifi4_engine *hifi4_engine = file->private_data; - struct fsl_hifi4 *hifi4_priv = hifi4_engine->hifi4_priv; - void __user *user = (void __user *)arg; + struct fsl_hifi4_engine *hifi4_engine; + struct fsl_hifi4 *hifi4_priv; + void __user *user; long ret = 0; + hifi4_engine = file->private_data; + hifi4_priv = hifi4_engine->hifi4_priv; + user = (void __user *)arg; + + mutex_lock(&hifi4_priv->hifi4_mutex); + switch (cmd) { case HIFI4_LOAD_CODEC: ret = fsl_hifi4_load_codec(hifi4_priv, user); break; case HIFI4_INIT_CODEC: - ret = fsl_hifi4_init_codec(hifi4_priv); + ret = fsl_hifi4_init_codec(hifi4_priv, user); break; case HIFI4_CODEC_OPEN: - ret = fsl_hifi4_codec_open(hifi4_priv); + ret = fsl_hifi4_codec_open(hifi4_priv, user); break; case HIFI4_DECODE_ONE_FRAME: ret = fsl_hifi4_decode_frame(hifi4_priv, user); break; case HIFI4_CODEC_CLOSE: - ret = fsl_hifi4_codec_close(hifi4_priv); + ret = fsl_hifi4_codec_close(hifi4_priv, user); break; case HIFI4_UNLOAD_CODEC: break; @@ -1103,6 +1401,8 @@ static long fsl_hifi4_ioctl(struct file *file, unsigned int cmd, break; } + mutex_unlock(&hifi4_priv->hifi4_mutex); + return ret; } @@ -1110,26 +1410,32 @@ static long fsl_hifi4_ioctl(struct file *file, unsigned int cmd, static long fsl_hifi4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct fsl_hifi4_engine *hifi4_engine = file->private_data; - struct fsl_hifi4 *hifi4_priv = hifi4_engine->hifi4_priv; - void __user *user = compat_ptr(arg); + struct fsl_hifi4_engine *hifi4_engine; + struct fsl_hifi4 *hifi4_priv; + void __user *user; long ret = 0; + hifi4_engine = file->private_data; + hifi4_priv = hifi4_engine->hifi4_priv; + user = compat_ptr(arg); + + mutex_lock(&hifi4_priv->hifi4_mutex); + switch (cmd) { case HIFI4_LOAD_CODEC: ret = fsl_hifi4_load_codec_compat32(hifi4_priv, user); break; case HIFI4_INIT_CODEC: - ret = fsl_hifi4_init_codec(hifi4_priv); + ret = fsl_hifi4_init_codec(hifi4_priv, user); break; case HIFI4_CODEC_OPEN: - ret = fsl_hifi4_codec_open(hifi4_priv); + ret = fsl_hifi4_codec_open(hifi4_priv, user); break; case HIFI4_DECODE_ONE_FRAME: ret = fsl_hifi4_decode_frame_compat32(hifi4_priv, user); break; case HIFI4_CODEC_CLOSE: - ret = fsl_hifi4_codec_close(hifi4_priv); + ret = fsl_hifi4_codec_close(hifi4_priv, user); break; case HIFI4_UNLOAD_CODEC: break; @@ -1143,17 +1449,24 @@ static long fsl_hifi4_compat_ioctl(struct file *file, unsigned int cmd, break; } + mutex_unlock(&hifi4_priv->hifi4_mutex); + return ret; } #endif static int fsl_hifi4_open(struct inode *inode, struct file *file) { - struct fsl_hifi4 *hifi4_priv = dev_get_drvdata(hifi4_miscdev.parent); - struct device *dev = hifi4_priv->dev; + struct fsl_hifi4 *hifi4_priv; + struct device *dev; struct fsl_hifi4_engine *hifi4_engine; int ret = 0; + hifi4_priv = dev_get_drvdata(hifi4_miscdev.parent); + dev = hifi4_priv->dev; + + mutex_lock(&hifi4_priv->hifi4_mutex); + hifi4_engine = devm_kzalloc(dev, sizeof(struct fsl_hifi4_engine), GFP_KERNEL); if (!hifi4_engine) @@ -1181,19 +1494,26 @@ static int fsl_hifi4_open(struct inode *inode, struct file *file) return ret; dev_info(dev, "hifi driver registered\n"); } + mutex_unlock(&hifi4_priv->hifi4_mutex); return ret; } static int fsl_hifi4_close(struct inode *inode, struct file *file) { - struct fsl_hifi4 *hifi4_priv = dev_get_drvdata(hifi4_miscdev.parent); - struct device *dev = hifi4_priv->dev; + struct fsl_hifi4 *hifi4_priv; + struct device *dev; struct fsl_hifi4_engine *hifi4_engine; + hifi4_priv = dev_get_drvdata(hifi4_miscdev.parent); + mutex_lock(&hifi4_priv->hifi4_mutex); + + dev = hifi4_priv->dev; hifi4_engine = file->private_data; devm_kfree(dev, hifi4_engine); + mutex_unlock(&hifi4_priv->hifi4_mutex); + return 0; } @@ -1298,8 +1618,10 @@ int process_act_complete(struct fsl_hifi4 *hifi4_priv, u32 msg) switch (recd_msg.sub_msg) { case ICM_PI_LIB_MEM_ALLOC: - hifi4_priv->is_done = 1; - complete(&hifi4_priv->cmd_complete); + { + hifi4_priv->is_done = 1; + complete(&hifi4_priv->cmd_complete); + } break; case ICM_OPEN: @@ -1307,10 +1629,10 @@ int process_act_complete(struct fsl_hifi4 *hifi4_priv, u32 msg) struct icm_open_resp_info_t *pext_msg = (struct icm_open_resp_info_t *)pmsg_apu; codec_iobuf_info->proc_id = pext_msg->proc_id; - hifi4_priv->is_done = 1; hifi4_priv->dpu_tstamp = (struct timestamp_info_t *)pext_msg->dtstamp; hifi4_priv->ret_status = pext_msg->ret; + hifi4_priv->is_done = 1; complete(&hifi4_priv->cmd_complete); } break; @@ -1326,9 +1648,9 @@ int process_act_complete(struct fsl_hifi4 *hifi4_priv, u32 msg) codec_iobuf_info->cycles = ext_msg->cycles; } - complete(&hifi4_priv->cmd_complete); - hifi4_priv->is_done = 1; hifi4_priv->ret_status = ext_msg->ret; + hifi4_priv->is_done = 1; + complete(&hifi4_priv->cmd_complete); } break; @@ -1343,10 +1665,10 @@ int process_act_complete(struct fsl_hifi4 *hifi4_priv, u32 msg) pcm_prop_info->bits = ext_msg->bits; pcm_prop_info->consumed_bytes = ext_msg->consumed_bytes; + hifi4_priv->ret_status = ext_msg->ret; - complete(&hifi4_priv->cmd_complete); hifi4_priv->is_done = 1; - hifi4_priv->ret_status = ext_msg->ret; + complete(&hifi4_priv->cmd_complete); } break; @@ -1354,20 +1676,30 @@ int process_act_complete(struct fsl_hifi4 *hifi4_priv, u32 msg) { struct prop_config *ext_msg = (struct prop_config *)pmsg_apu; - complete(&hifi4_priv->cmd_complete); - hifi4_priv->is_done = 1; hifi4_priv->ret_status = ext_msg->ret; + hifi4_priv->is_done = 1; + complete(&hifi4_priv->cmd_complete); } break; case ICM_PI_LIB_INIT: - complete(&hifi4_priv->cmd_complete); hifi4_priv->is_done = 1; + complete(&hifi4_priv->cmd_complete); break; case ICM_CLOSE: - complete(&hifi4_priv->cmd_complete); hifi4_priv->is_done = 1; + complete(&hifi4_priv->cmd_complete); + break; + + case ICM_SWITCH_CODEC: + { + struct icm_switch_info_t *ext_msg = + (struct icm_switch_info_t *)pmsg_apu; + hifi4_priv->ret_status = ext_msg->status; + hifi4_priv->is_done = 1; + complete(&hifi4_priv->cmd_complete); + } break; default: @@ -1391,7 +1723,8 @@ int send_dpu_ext_msg_addr(struct fsl_hifi4 *hifi4_priv) apu_icm.msg = ICM_EXT_MSG_ADDR; apu_icm.size = 8; ext_msg.phys = hifi4_priv->msg_buf_phys; - ext_msg.size = 8*4; + /* 10 means variable numbers that need to be transferred */ + ext_msg.size = 10*4; /* 10 * sizeof(int) */ dpu_ext_msg->ext_msg_phys = hifi4_priv->msg_buf_phys + 2048; dpu_ext_msg->ext_msg_size = 2048; dpu_ext_msg->code_phys = hifi4_priv->code_buf_phys; @@ -1400,6 +1733,8 @@ int send_dpu_ext_msg_addr(struct fsl_hifi4 *hifi4_priv) dpu_ext_msg->data_size = hifi4_priv->data_buf_size; dpu_ext_msg->scratch_phys = hifi4_priv->scratch_buf_phys; dpu_ext_msg->scratch_size = hifi4_priv->scratch_buf_size; + dpu_ext_msg->system_input_buf_phys = hifi4_priv->in_buf_phys; + dpu_ext_msg->system_input_buf_size = hifi4_priv->in_buf_size; icm_intr_extended_send(hifi4_priv, apu_icm.allbits, &ext_msg); @@ -1592,7 +1927,7 @@ static int fsl_hifi4_probe(struct platform_device *pdev) sc_err_t sciErr; void *buf_virt; dma_addr_t buf_phys; - int size, offset; + int size, offset, i; int ret; hifi4_priv = devm_kzalloc(&pdev->dev, sizeof(*hifi4_priv), GFP_KERNEL); @@ -1730,6 +2065,30 @@ static int fsl_hifi4_probe(struct platform_device *pdev) hifi4_priv->scratch_buf_phys = buf_phys + offset; hifi4_priv->scratch_buf_size = SCRATCH_DATA_BUF_SIZE; + /* process_id_count is a counter to produce new id + * process_id is current codec's id + */ + hifi4_priv->process_id_count = 0; + hifi4_priv->process_id = 0; + + /* initialize the resources of multi codec + * MULTI_CODEC_NUM is the max codec number that dsp + * driver and framework can support. + */ + hifi4_priv->available_resource = MULTI_CODEC_NUM; + for (i = 0; i < MULTI_CODEC_NUM; i++) { + hifi4_priv->process_info[i].data_buf_virt = + hifi4_priv->data_buf_virt + + i * hifi4_priv->data_buf_size / MULTI_CODEC_NUM; + hifi4_priv->process_info[i].data_buf_phys = + hifi4_priv->data_buf_phys + + i * hifi4_priv->data_buf_size / MULTI_CODEC_NUM; + + hifi4_priv->process_info[i].status = 0; + } + + mutex_init(&hifi4_priv->hifi4_mutex); + return 0; } diff --git a/sound/soc/fsl/fsl_hifi4.h b/sound/soc/fsl/fsl_hifi4.h index 3cb835654f06204ceb4b3ea4472499bb6479a195..d28666b311711514395d3f5e216399457b1d5842 100644 --- a/sound/soc/fsl/fsl_hifi4.h +++ b/sound/soc/fsl/fsl_hifi4.h @@ -21,6 +21,7 @@ typedef void (*memcpy_func) (void *dest, const void *src, size_t n); typedef void (*memset_func) (void *s, int c, size_t n); struct xtlib_packaged_library; +#define MULTI_CODEC_NUM 5 enum { XTLIB_NO_ERR = 0, @@ -61,8 +62,6 @@ struct icm_cdc_iobuf_t { struct icm_cdc_uinp_t { u32 proc_id; /* audio id */ u32 codec_id; /* codec identifier */ - u32 pcm_wd_sz; /* pcm word size; only 16 or 24 */ - u32 crc_check; /* 0: disable, 1: enable */ }; struct icm_pcm_prop_t { @@ -134,6 +133,8 @@ enum icm_action_t { ICM_CORE_EXIT, ICM_EXT_MSG_ADDR, + + ICM_SWITCH_CODEC, }; enum aud_status_t { @@ -150,6 +151,11 @@ struct icm_open_resp_info_t { s32 ret; }; +struct icm_switch_info_t { + u32 proc_id; /* audio id */ + u32 status; /* codec status */ +}; + struct lib_dnld_info_t { unsigned long pbuf_code; unsigned long pbuf_data; @@ -166,6 +172,18 @@ struct icm_pilib_size_t { u32 data_size; }; +struct icm_process_info { + unsigned int process_id; + unsigned int codec_id; + unsigned int proc_id; + + void *data_buf_virt; + dma_addr_t data_buf_phys; + + struct xtlib_pil_info pil_info_info; + unsigned int status; +}; + struct fsl_hifi4 { struct device *dev; const char *fw_name; @@ -214,6 +232,18 @@ struct fsl_hifi4 { struct xtlib_pil_info pil_info; struct xtlib_loader_globals xtlib_globals; struct timestamp_info_t *dpu_tstamp; + + struct mutex hifi4_mutex; + + unsigned int process_id; + unsigned int process_id_count; + + unsigned int size_code; + unsigned int size_data; + + struct icm_process_info process_info[MULTI_CODEC_NUM]; + unsigned int available_resource; + unsigned int cur_res_id; }; struct fsl_hifi4_engine { @@ -234,6 +264,8 @@ struct hifi4_mem_msg { u32 data_size; u32 scratch_phys; u32 scratch_size; + u32 system_input_buf_phys; + u32 system_input_buf_size; }; #define IRAM_OFFSET 0x10000 @@ -257,8 +289,8 @@ struct hifi4_mem_msg { #define MSG_BUF_SIZE 4096 #define INPUT_BUF_SIZE 4096 #define OUTPUT_BUF_SIZE 16384 -#define FIRMWARE_DATA_BUF_SIZE 0x100000 -#define SCRATCH_DATA_BUF_SIZE 0x100000 +#define FIRMWARE_DATA_BUF_SIZE (MULTI_CODEC_NUM * 0x80000) +#define SCRATCH_DATA_BUF_SIZE (MULTI_CODEC_NUM * 0x80000) #define MEMORY_REMAP_OFFSET 0x39000000