twl4030-codec.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*
  2. * MFD driver for twl4030 codec submodule
  3. *
  4. * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
  5. *
  6. * Copyright: (C) 2009 Nokia Corporation
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License version 2 as
  10. * published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA
  21. *
  22. */
  23. #include <linux/module.h>
  24. #include <linux/types.h>
  25. #include <linux/kernel.h>
  26. #include <linux/fs.h>
  27. #include <linux/platform_device.h>
  28. #include <linux/i2c/twl4030.h>
  29. #include <linux/mfd/core.h>
  30. #include <linux/mfd/twl4030-codec.h>
  31. #define TWL4030_CODEC_CELLS 2
  32. static struct platform_device *twl4030_codec_dev;
  33. struct twl4030_codec_resource {
  34. int request_count;
  35. u8 reg;
  36. u8 mask;
  37. };
  38. struct twl4030_codec {
  39. struct mutex mutex;
  40. struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX];
  41. struct mfd_cell cells[TWL4030_CODEC_CELLS];
  42. };
  43. /*
  44. * Modify the resource, the function returns the content of the register
  45. * after the modification.
  46. */
  47. static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable)
  48. {
  49. struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
  50. u8 val;
  51. twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
  52. codec->resource[id].reg);
  53. if (enable)
  54. val |= codec->resource[id].mask;
  55. else
  56. val &= ~codec->resource[id].mask;
  57. twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
  58. val, codec->resource[id].reg);
  59. return val;
  60. }
  61. static inline int twl4030_codec_get_resource(enum twl4030_codec_res id)
  62. {
  63. struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
  64. u8 val;
  65. twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
  66. codec->resource[id].reg);
  67. return val;
  68. }
  69. /*
  70. * Enable the resource.
  71. * The function returns with error or the content of the register
  72. */
  73. int twl4030_codec_enable_resource(enum twl4030_codec_res id)
  74. {
  75. struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
  76. int val;
  77. if (id >= TWL4030_CODEC_RES_MAX) {
  78. dev_err(&twl4030_codec_dev->dev,
  79. "Invalid resource ID (%u)\n", id);
  80. return -EINVAL;
  81. }
  82. mutex_lock(&codec->mutex);
  83. if (!codec->resource[id].request_count)
  84. /* Resource was disabled, enable it */
  85. val = twl4030_codec_set_resource(id, 1);
  86. else
  87. val = twl4030_codec_get_resource(id);
  88. codec->resource[id].request_count++;
  89. mutex_unlock(&codec->mutex);
  90. return val;
  91. }
  92. EXPORT_SYMBOL_GPL(twl4030_codec_enable_resource);
  93. /*
  94. * Disable the resource.
  95. * The function returns with error or the content of the register
  96. */
  97. int twl4030_codec_disable_resource(unsigned id)
  98. {
  99. struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
  100. int val;
  101. if (id >= TWL4030_CODEC_RES_MAX) {
  102. dev_err(&twl4030_codec_dev->dev,
  103. "Invalid resource ID (%u)\n", id);
  104. return -EINVAL;
  105. }
  106. mutex_lock(&codec->mutex);
  107. if (!codec->resource[id].request_count) {
  108. dev_err(&twl4030_codec_dev->dev,
  109. "Resource has been disabled already (%u)\n", id);
  110. mutex_unlock(&codec->mutex);
  111. return -EPERM;
  112. }
  113. codec->resource[id].request_count--;
  114. if (!codec->resource[id].request_count)
  115. /* Resource can be disabled now */
  116. val = twl4030_codec_set_resource(id, 0);
  117. else
  118. val = twl4030_codec_get_resource(id);
  119. mutex_unlock(&codec->mutex);
  120. return val;
  121. }
  122. EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource);
  123. static int __devinit twl4030_codec_probe(struct platform_device *pdev)
  124. {
  125. struct twl4030_codec *codec;
  126. struct twl4030_codec_data *pdata = pdev->dev.platform_data;
  127. struct mfd_cell *cell = NULL;
  128. int ret, childs = 0;
  129. codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL);
  130. if (!codec)
  131. return -ENOMEM;
  132. platform_set_drvdata(pdev, codec);
  133. twl4030_codec_dev = pdev;
  134. mutex_init(&codec->mutex);
  135. /* Codec power */
  136. codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE;
  137. codec->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ;
  138. /* PLL */
  139. codec->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL;
  140. codec->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN;
  141. if (pdata->audio) {
  142. cell = &codec->cells[childs];
  143. cell->name = "twl4030_codec_audio";
  144. cell->platform_data = pdata->audio;
  145. cell->data_size = sizeof(*pdata->audio);
  146. childs++;
  147. }
  148. if (pdata->vibra) {
  149. cell = &codec->cells[childs];
  150. cell->name = "twl4030_codec_vibra";
  151. cell->platform_data = pdata->vibra;
  152. cell->data_size = sizeof(*pdata->vibra);
  153. childs++;
  154. }
  155. if (childs)
  156. ret = mfd_add_devices(&pdev->dev, pdev->id, codec->cells,
  157. childs, NULL, 0);
  158. else {
  159. dev_err(&pdev->dev, "No platform data found for childs\n");
  160. ret = -ENODEV;
  161. }
  162. if (!ret)
  163. return 0;
  164. platform_set_drvdata(pdev, NULL);
  165. kfree(codec);
  166. twl4030_codec_dev = NULL;
  167. return ret;
  168. }
  169. static int __devexit twl4030_codec_remove(struct platform_device *pdev)
  170. {
  171. struct twl4030_codec *codec = platform_get_drvdata(pdev);
  172. mfd_remove_devices(&pdev->dev);
  173. platform_set_drvdata(pdev, NULL);
  174. kfree(codec);
  175. twl4030_codec_dev = NULL;
  176. return 0;
  177. }
  178. MODULE_ALIAS("platform:twl4030_codec");
  179. static struct platform_driver twl4030_codec_driver = {
  180. .probe = twl4030_codec_probe,
  181. .remove = __devexit_p(twl4030_codec_remove),
  182. .driver = {
  183. .owner = THIS_MODULE,
  184. .name = "twl4030_codec",
  185. },
  186. };
  187. static int __devinit twl4030_codec_init(void)
  188. {
  189. return platform_driver_register(&twl4030_codec_driver);
  190. }
  191. module_init(twl4030_codec_init);
  192. static void __devexit twl4030_codec_exit(void)
  193. {
  194. platform_driver_unregister(&twl4030_codec_driver);
  195. }
  196. module_exit(twl4030_codec_exit);
  197. MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
  198. MODULE_LICENSE("GPL");