pt2258.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * ALSA Driver for the PT2258 volume controller.
  4. *
  5. * Copyright (c) 2006 Jochen Voss <[email protected]>
  6. */
  7. #include <sound/core.h>
  8. #include <sound/control.h>
  9. #include <sound/tlv.h>
  10. #include <sound/i2c.h>
  11. #include <sound/pt2258.h>
  12. #include <linux/module.h>
  13. MODULE_AUTHOR("Jochen Voss <[email protected]>");
  14. MODULE_DESCRIPTION("PT2258 volume controller (Princeton Technology Corp.)");
  15. MODULE_LICENSE("GPL");
  16. #define PT2258_CMD_RESET 0xc0
  17. #define PT2258_CMD_UNMUTE 0xf8
  18. #define PT2258_CMD_MUTE 0xf9
  19. static const unsigned char pt2258_channel_code[12] = {
  20. 0x80, 0x90, /* channel 1: -10dB, -1dB */
  21. 0x40, 0x50, /* channel 2: -10dB, -1dB */
  22. 0x00, 0x10, /* channel 3: -10dB, -1dB */
  23. 0x20, 0x30, /* channel 4: -10dB, -1dB */
  24. 0x60, 0x70, /* channel 5: -10dB, -1dB */
  25. 0xa0, 0xb0 /* channel 6: -10dB, -1dB */
  26. };
  27. int snd_pt2258_reset(struct snd_pt2258 *pt)
  28. {
  29. unsigned char bytes[2];
  30. int i;
  31. /* reset chip */
  32. bytes[0] = PT2258_CMD_RESET;
  33. snd_i2c_lock(pt->i2c_bus);
  34. if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1)
  35. goto __error;
  36. snd_i2c_unlock(pt->i2c_bus);
  37. /* mute all channels */
  38. pt->mute = 1;
  39. bytes[0] = PT2258_CMD_MUTE;
  40. snd_i2c_lock(pt->i2c_bus);
  41. if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1)
  42. goto __error;
  43. snd_i2c_unlock(pt->i2c_bus);
  44. /* set all channels to 0dB */
  45. for (i = 0; i < 6; ++i)
  46. pt->volume[i] = 0;
  47. bytes[0] = 0xd0;
  48. bytes[1] = 0xe0;
  49. snd_i2c_lock(pt->i2c_bus);
  50. if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2)
  51. goto __error;
  52. snd_i2c_unlock(pt->i2c_bus);
  53. return 0;
  54. __error:
  55. snd_i2c_unlock(pt->i2c_bus);
  56. snd_printk(KERN_ERR "PT2258 reset failed\n");
  57. return -EIO;
  58. }
  59. static int pt2258_stereo_volume_info(struct snd_kcontrol *kcontrol,
  60. struct snd_ctl_elem_info *uinfo)
  61. {
  62. uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  63. uinfo->count = 2;
  64. uinfo->value.integer.min = 0;
  65. uinfo->value.integer.max = 79;
  66. return 0;
  67. }
  68. static int pt2258_stereo_volume_get(struct snd_kcontrol *kcontrol,
  69. struct snd_ctl_elem_value *ucontrol)
  70. {
  71. struct snd_pt2258 *pt = kcontrol->private_data;
  72. int base = kcontrol->private_value;
  73. /* chip does not support register reads */
  74. ucontrol->value.integer.value[0] = 79 - pt->volume[base];
  75. ucontrol->value.integer.value[1] = 79 - pt->volume[base + 1];
  76. return 0;
  77. }
  78. static int pt2258_stereo_volume_put(struct snd_kcontrol *kcontrol,
  79. struct snd_ctl_elem_value *ucontrol)
  80. {
  81. struct snd_pt2258 *pt = kcontrol->private_data;
  82. int base = kcontrol->private_value;
  83. unsigned char bytes[2];
  84. int val0, val1;
  85. val0 = 79 - ucontrol->value.integer.value[0];
  86. val1 = 79 - ucontrol->value.integer.value[1];
  87. if (val0 < 0 || val0 > 79 || val1 < 0 || val1 > 79)
  88. return -EINVAL;
  89. if (val0 == pt->volume[base] && val1 == pt->volume[base + 1])
  90. return 0;
  91. pt->volume[base] = val0;
  92. bytes[0] = pt2258_channel_code[2 * base] | (val0 / 10);
  93. bytes[1] = pt2258_channel_code[2 * base + 1] | (val0 % 10);
  94. snd_i2c_lock(pt->i2c_bus);
  95. if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2)
  96. goto __error;
  97. snd_i2c_unlock(pt->i2c_bus);
  98. pt->volume[base + 1] = val1;
  99. bytes[0] = pt2258_channel_code[2 * base + 2] | (val1 / 10);
  100. bytes[1] = pt2258_channel_code[2 * base + 3] | (val1 % 10);
  101. snd_i2c_lock(pt->i2c_bus);
  102. if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2)
  103. goto __error;
  104. snd_i2c_unlock(pt->i2c_bus);
  105. return 1;
  106. __error:
  107. snd_i2c_unlock(pt->i2c_bus);
  108. snd_printk(KERN_ERR "PT2258 access failed\n");
  109. return -EIO;
  110. }
  111. #define pt2258_switch_info snd_ctl_boolean_mono_info
  112. static int pt2258_switch_get(struct snd_kcontrol *kcontrol,
  113. struct snd_ctl_elem_value *ucontrol)
  114. {
  115. struct snd_pt2258 *pt = kcontrol->private_data;
  116. ucontrol->value.integer.value[0] = !pt->mute;
  117. return 0;
  118. }
  119. static int pt2258_switch_put(struct snd_kcontrol *kcontrol,
  120. struct snd_ctl_elem_value *ucontrol)
  121. {
  122. struct snd_pt2258 *pt = kcontrol->private_data;
  123. unsigned char bytes[2];
  124. int val;
  125. val = !ucontrol->value.integer.value[0];
  126. if (pt->mute == val)
  127. return 0;
  128. pt->mute = val;
  129. bytes[0] = val ? PT2258_CMD_MUTE : PT2258_CMD_UNMUTE;
  130. snd_i2c_lock(pt->i2c_bus);
  131. if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1)
  132. goto __error;
  133. snd_i2c_unlock(pt->i2c_bus);
  134. return 1;
  135. __error:
  136. snd_i2c_unlock(pt->i2c_bus);
  137. snd_printk(KERN_ERR "PT2258 access failed 2\n");
  138. return -EIO;
  139. }
  140. static const DECLARE_TLV_DB_SCALE(pt2258_db_scale, -7900, 100, 0);
  141. int snd_pt2258_build_controls(struct snd_pt2258 *pt)
  142. {
  143. struct snd_kcontrol_new knew;
  144. char *names[3] = {
  145. "Mic Loopback Playback Volume",
  146. "Line Loopback Playback Volume",
  147. "CD Loopback Playback Volume"
  148. };
  149. int i, err;
  150. for (i = 0; i < 3; ++i) {
  151. memset(&knew, 0, sizeof(knew));
  152. knew.name = names[i];
  153. knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
  154. knew.count = 1;
  155. knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
  156. SNDRV_CTL_ELEM_ACCESS_TLV_READ;
  157. knew.private_value = 2 * i;
  158. knew.info = pt2258_stereo_volume_info;
  159. knew.get = pt2258_stereo_volume_get;
  160. knew.put = pt2258_stereo_volume_put;
  161. knew.tlv.p = pt2258_db_scale;
  162. err = snd_ctl_add(pt->card, snd_ctl_new1(&knew, pt));
  163. if (err < 0)
  164. return err;
  165. }
  166. memset(&knew, 0, sizeof(knew));
  167. knew.name = "Loopback Switch";
  168. knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
  169. knew.info = pt2258_switch_info;
  170. knew.get = pt2258_switch_get;
  171. knew.put = pt2258_switch_put;
  172. knew.access = 0;
  173. err = snd_ctl_add(pt->card, snd_ctl_new1(&knew, pt));
  174. if (err < 0)
  175. return err;
  176. return 0;
  177. }
  178. EXPORT_SYMBOL(snd_pt2258_reset);
  179. EXPORT_SYMBOL(snd_pt2258_build_controls);