wcdcal-hwdep.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2015, 2017-2018 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. static int wcdcal_hwdep_ioctl_shared(struct snd_hwdep *hw,
  52. struct wcdcal_ioctl_buffer fw_user)
  53. {
  54. struct fw_info *fw_data = hw->private_data;
  55. struct firmware_cal **fw = fw_data->fw;
  56. void *data;
  57. if (!test_bit(fw_user.cal_type, fw_data->cal_bit)) {
  58. pr_err("%s: codec didn't set this %d!!\n",
  59. __func__, fw_user.cal_type);
  60. return -EFAULT;
  61. }
  62. if (fw_user.cal_type >= WCD9XXX_MAX_CAL ||
  63. fw_user.cal_type < WCD9XXX_MIN_CAL) {
  64. pr_err("%s: wrong cal type sent %d\n",
  65. __func__, fw_user.cal_type);
  66. return -EFAULT;
  67. }
  68. if (fw_user.size > cal_size_info[fw_user.cal_type] ||
  69. fw_user.size <= 0) {
  70. pr_err("%s: incorrect firmware size %d for %s\n",
  71. __func__, fw_user.size,
  72. cal_name_info[fw_user.cal_type]);
  73. return -EFAULT;
  74. }
  75. data = fw[fw_user.cal_type]->data;
  76. if (copy_from_user(data, fw_user.buffer, fw_user.size))
  77. return -EFAULT;
  78. fw[fw_user.cal_type]->size = fw_user.size;
  79. mutex_lock(&fw_data->lock);
  80. set_bit(WCDCAL_RECIEVED, &fw_data->wcdcal_state[fw_user.cal_type]);
  81. mutex_unlock(&fw_data->lock);
  82. return 0;
  83. }
  84. #ifdef CONFIG_COMPAT
  85. struct wcdcal_ioctl_buffer32 {
  86. u32 size;
  87. compat_uptr_t buffer;
  88. enum wcd_cal_type cal_type;
  89. };
  90. enum {
  91. SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32 =
  92. _IOW('U', 0x1, struct wcdcal_ioctl_buffer32),
  93. };
  94. static int wcdcal_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
  95. unsigned int cmd, unsigned long arg)
  96. {
  97. struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
  98. struct wcdcal_ioctl_buffer32 fw_user32;
  99. struct wcdcal_ioctl_buffer fw_user_compat;
  100. if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32) {
  101. pr_err("%s: wrong ioctl command sent %u!\n", __func__, cmd);
  102. return -ENOIOCTLCMD;
  103. }
  104. if (copy_from_user(&fw_user32, argp, sizeof(fw_user32))) {
  105. pr_err("%s: failed to copy\n", __func__);
  106. return -EFAULT;
  107. }
  108. fw_user_compat.size = fw_user32.size;
  109. fw_user_compat.buffer = compat_ptr(fw_user32.buffer);
  110. fw_user_compat.cal_type = fw_user32.cal_type;
  111. return wcdcal_hwdep_ioctl_shared(hw, fw_user_compat);
  112. }
  113. #else
  114. #define wcdcal_hwdep_ioctl_compat NULL
  115. #endif
  116. static int wcdcal_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
  117. unsigned int cmd, unsigned long arg)
  118. {
  119. struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
  120. struct wcdcal_ioctl_buffer fw_user;
  121. if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE) {
  122. pr_err("%s: wrong ioctl command sent %d!\n", __func__, cmd);
  123. return -ENOIOCTLCMD;
  124. }
  125. if (copy_from_user(&fw_user, argp, sizeof(fw_user))) {
  126. pr_err("%s: failed to copy\n", __func__);
  127. return -EFAULT;
  128. }
  129. return wcdcal_hwdep_ioctl_shared(hw, fw_user);
  130. }
  131. static int wcdcal_hwdep_release(struct snd_hwdep *hw, struct file *file)
  132. {
  133. struct fw_info *fw_data = hw->private_data;
  134. mutex_lock(&fw_data->lock);
  135. /* clear all the calibrations */
  136. memset(fw_data->wcdcal_state, 0,
  137. sizeof(fw_data->wcdcal_state));
  138. mutex_unlock(&fw_data->lock);
  139. return 0;
  140. }
  141. int wcd_cal_create_hwdep(void *data, int node,
  142. struct snd_soc_component *component)
  143. {
  144. char hwname[40];
  145. struct snd_hwdep *hwdep;
  146. struct firmware_cal **fw;
  147. struct fw_info *fw_data = data;
  148. int err, cal_bit;
  149. if (!fw_data || !component) {
  150. pr_err("%s: wrong arguments passed\n", __func__);
  151. return -EINVAL;
  152. }
  153. fw = fw_data->fw;
  154. snprintf(hwname, strlen("Codec %s"), "Codec %s",
  155. component->name);
  156. err = snd_hwdep_new(component->card->snd_card,
  157. hwname, node, &hwdep);
  158. if (err < 0) {
  159. dev_err(component->dev, "%s: new hwdep failed %d\n",
  160. __func__, err);
  161. return err;
  162. }
  163. snprintf(hwdep->name, strlen("Codec %s"), "Codec %s",
  164. component->name);
  165. hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_CODEC;
  166. hwdep->private_data = fw_data;
  167. hwdep->ops.ioctl_compat = wcdcal_hwdep_ioctl_compat;
  168. hwdep->ops.ioctl = wcdcal_hwdep_ioctl;
  169. hwdep->ops.release = wcdcal_hwdep_release;
  170. mutex_init(&fw_data->lock);
  171. for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
  172. set_bit(WCDCAL_UNINITIALISED,
  173. &fw_data->wcdcal_state[cal_bit]);
  174. fw[cal_bit] = kzalloc(sizeof *(fw[cal_bit]), GFP_KERNEL);
  175. if (!fw[cal_bit])
  176. goto end;
  177. }
  178. for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
  179. fw[cal_bit]->data = kzalloc(cal_size_info[cal_bit],
  180. GFP_KERNEL);
  181. if (!fw[cal_bit]->data)
  182. goto exit;
  183. set_bit(WCDCAL_INITIALISED,
  184. &fw_data->wcdcal_state[cal_bit]);
  185. }
  186. return 0;
  187. exit:
  188. for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
  189. kfree(fw[cal_bit]->data);
  190. fw[cal_bit]->data = NULL;
  191. }
  192. end:
  193. for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
  194. kfree(fw[cal_bit]);
  195. fw[cal_bit] = NULL;
  196. }
  197. return -ENOMEM;
  198. }
  199. EXPORT_SYMBOL(wcd_cal_create_hwdep);