wcdcal-hwdep.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2015, 2017-2018, 2020 The Linux Foundation. All rights reserved.
  4. *
  5. */
  6. #include <linux/kernel.h>
  7. #include <linux/platform_device.h>
  8. #include <linux/slab.h>
  9. #include <linux/ioctl.h>
  10. #include <linux/bitops.h>
  11. #include <sound/hwdep.h>
  12. #include <sound/msmcal-hwdep.h>
  13. #include <sound/soc.h>
  14. #include <asoc/wcdcal-hwdep.h>
  15. const int cal_size_info[WCD9XXX_MAX_CAL] = {
  16. [WCD9XXX_ANC_CAL] = 16384,
  17. [WCD9XXX_MBHC_CAL] = 4096,
  18. [WCD9XXX_MAD_CAL] = 4096,
  19. [WCD9XXX_VBAT_CAL] = 72,
  20. };
  21. const char *cal_name_info[WCD9XXX_MAX_CAL] = {
  22. [WCD9XXX_ANC_CAL] = "anc",
  23. [WCD9XXX_MBHC_CAL] = "mbhc",
  24. [WCD9XXX_MAD_CAL] = "mad",
  25. [WCD9XXX_VBAT_CAL] = "vbat",
  26. };
  27. struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data,
  28. enum wcd_cal_type type)
  29. {
  30. if (!fw_data) {
  31. pr_err("%s: fw_data is NULL\n", __func__);
  32. return NULL;
  33. }
  34. if (type >= WCD9XXX_MAX_CAL ||
  35. type < WCD9XXX_MIN_CAL) {
  36. pr_err("%s: wrong cal type sent %d\n", __func__, type);
  37. return NULL;
  38. }
  39. mutex_lock(&fw_data->lock);
  40. if (!test_bit(WCDCAL_RECIEVED,
  41. &fw_data->wcdcal_state[type])) {
  42. pr_err("%s: cal not sent by userspace %d\n",
  43. __func__, type);
  44. mutex_unlock(&fw_data->lock);
  45. return NULL;
  46. }
  47. mutex_unlock(&fw_data->lock);
  48. return fw_data->fw[type];
  49. }
  50. EXPORT_SYMBOL(wcdcal_get_fw_cal);
  51. #if IS_ENABLED(CONFIG_AUDIO_QGKI)
  52. static int wcdcal_hwdep_ioctl_shared(struct snd_hwdep *hw,
  53. struct wcdcal_ioctl_buffer fw_user)
  54. {
  55. struct fw_info *fw_data = hw->private_data;
  56. struct firmware_cal **fw = fw_data->fw;
  57. void *data;
  58. if (!test_bit(fw_user.cal_type, fw_data->cal_bit)) {
  59. pr_err("%s: codec didn't set this %d!!\n",
  60. __func__, fw_user.cal_type);
  61. return -EFAULT;
  62. }
  63. if (fw_user.cal_type >= WCD9XXX_MAX_CAL ||
  64. fw_user.cal_type < WCD9XXX_MIN_CAL) {
  65. pr_err("%s: wrong cal type sent %d\n",
  66. __func__, fw_user.cal_type);
  67. return -EFAULT;
  68. }
  69. if (fw_user.size > cal_size_info[fw_user.cal_type] ||
  70. fw_user.size <= 0) {
  71. pr_err("%s: incorrect firmware size %d for %s\n",
  72. __func__, fw_user.size,
  73. cal_name_info[fw_user.cal_type]);
  74. return -EFAULT;
  75. }
  76. data = fw[fw_user.cal_type]->data;
  77. if (copy_from_user(data, fw_user.buffer, fw_user.size))
  78. return -EFAULT;
  79. fw[fw_user.cal_type]->size = fw_user.size;
  80. mutex_lock(&fw_data->lock);
  81. set_bit(WCDCAL_RECIEVED, &fw_data->wcdcal_state[fw_user.cal_type]);
  82. mutex_unlock(&fw_data->lock);
  83. return 0;
  84. }
  85. #endif /* CONFIG_AUDIO_QGKI */
  86. #ifdef CONFIG_COMPAT
  87. struct wcdcal_ioctl_buffer32 {
  88. u32 size;
  89. compat_uptr_t buffer;
  90. enum wcd_cal_type cal_type;
  91. };
  92. enum {
  93. SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32 =
  94. _IOW('U', 0x1, struct wcdcal_ioctl_buffer32),
  95. };
  96. #if IS_ENABLED(CONFIG_AUDIO_QGKI)
  97. static int wcdcal_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
  98. unsigned int cmd, unsigned long arg)
  99. {
  100. struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
  101. struct wcdcal_ioctl_buffer32 fw_user32;
  102. struct wcdcal_ioctl_buffer fw_user_compat;
  103. if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32) {
  104. pr_err("%s: wrong ioctl command sent %u!\n", __func__, cmd);
  105. return -ENOIOCTLCMD;
  106. }
  107. if (copy_from_user(&fw_user32, argp, sizeof(fw_user32))) {
  108. pr_err("%s: failed to copy\n", __func__);
  109. return -EFAULT;
  110. }
  111. fw_user_compat.size = fw_user32.size;
  112. fw_user_compat.buffer = compat_ptr(fw_user32.buffer);
  113. fw_user_compat.cal_type = fw_user32.cal_type;
  114. return wcdcal_hwdep_ioctl_shared(hw, fw_user_compat);
  115. }
  116. #endif /* CONFIG_AUDIO_QGKI */
  117. #else
  118. #define wcdcal_hwdep_ioctl_compat NULL
  119. #endif
  120. #if IS_ENABLED(CONFIG_AUDIO_QGKI)
  121. static int wcdcal_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
  122. unsigned int cmd, unsigned long arg)
  123. {
  124. struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
  125. struct wcdcal_ioctl_buffer fw_user;
  126. if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE) {
  127. pr_err("%s: wrong ioctl command sent %d!\n", __func__, cmd);
  128. return -ENOIOCTLCMD;
  129. }
  130. if (copy_from_user(&fw_user, argp, sizeof(fw_user))) {
  131. pr_err("%s: failed to copy\n", __func__);
  132. return -EFAULT;
  133. }
  134. return wcdcal_hwdep_ioctl_shared(hw, fw_user);
  135. }
  136. static int wcdcal_hwdep_release(struct snd_hwdep *hw, struct file *file)
  137. {
  138. struct fw_info *fw_data = hw->private_data;
  139. mutex_lock(&fw_data->lock);
  140. /* clear all the calibrations */
  141. memset(fw_data->wcdcal_state, 0,
  142. sizeof(fw_data->wcdcal_state));
  143. mutex_unlock(&fw_data->lock);
  144. return 0;
  145. }
  146. int wcd_cal_create_hwdep(void *data, int node,
  147. struct snd_soc_component *component)
  148. {
  149. char hwname[40];
  150. struct snd_hwdep *hwdep;
  151. struct firmware_cal **fw;
  152. struct fw_info *fw_data = data;
  153. int err, cal_bit;
  154. if (!fw_data || !component) {
  155. pr_err("%s: wrong arguments passed\n", __func__);
  156. return -EINVAL;
  157. }
  158. fw = fw_data->fw;
  159. snprintf(hwname, strlen("Codec %s"), "Codec %s",
  160. component->name);
  161. err = snd_hwdep_new(component->card->snd_card,
  162. hwname, node, &hwdep);
  163. if (err < 0) {
  164. dev_err(component->dev, "%s: new hwdep failed %d\n",
  165. __func__, err);
  166. return err;
  167. }
  168. snprintf(hwdep->name, strlen("Codec %s"), "Codec %s",
  169. component->name);
  170. hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_CODEC;
  171. hwdep->private_data = fw_data;
  172. hwdep->ops.ioctl_compat = wcdcal_hwdep_ioctl_compat;
  173. hwdep->ops.ioctl = wcdcal_hwdep_ioctl;
  174. hwdep->ops.release = wcdcal_hwdep_release;
  175. mutex_init(&fw_data->lock);
  176. for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
  177. set_bit(WCDCAL_UNINITIALISED,
  178. &fw_data->wcdcal_state[cal_bit]);
  179. fw[cal_bit] = kzalloc(sizeof *(fw[cal_bit]), GFP_KERNEL);
  180. if (!fw[cal_bit])
  181. goto end;
  182. }
  183. for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
  184. fw[cal_bit]->data = kzalloc(cal_size_info[cal_bit],
  185. GFP_KERNEL);
  186. if (!fw[cal_bit]->data)
  187. goto exit;
  188. set_bit(WCDCAL_INITIALISED,
  189. &fw_data->wcdcal_state[cal_bit]);
  190. }
  191. return 0;
  192. exit:
  193. for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
  194. kfree(fw[cal_bit]->data);
  195. fw[cal_bit]->data = NULL;
  196. }
  197. end:
  198. for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
  199. kfree(fw[cal_bit]);
  200. fw[cal_bit] = NULL;
  201. }
  202. return -ENOMEM;
  203. }
  204. #else
  205. int wcd_cal_create_hwdep(void *data, int node,
  206. struct snd_soc_component *component)
  207. {
  208. return 0;
  209. }
  210. #endif /* CONFIG_AUDIO_QGKI */
  211. EXPORT_SYMBOL(wcd_cal_create_hwdep);