solo6x10-tw28.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com>
  4. *
  5. * Original author:
  6. * Ben Collins <[email protected]>
  7. *
  8. * Additional work by:
  9. * John Brooks <[email protected]>
  10. */
  11. #include <linux/kernel.h>
  12. #include <linux/delay.h>
  13. #include "solo6x10.h"
  14. #include "solo6x10-tw28.h"
  15. #define DEFAULT_HDELAY_NTSC (32 - 8)
  16. #define DEFAULT_HACTIVE_NTSC (720 + 16)
  17. #define DEFAULT_VDELAY_NTSC (7 - 2)
  18. #define DEFAULT_VACTIVE_NTSC (240 + 4)
  19. #define DEFAULT_HDELAY_PAL (32 + 4)
  20. #define DEFAULT_HACTIVE_PAL (864-DEFAULT_HDELAY_PAL)
  21. #define DEFAULT_VDELAY_PAL (6)
  22. #define DEFAULT_VACTIVE_PAL (312-DEFAULT_VDELAY_PAL)
  23. static const u8 tbl_tw2864_ntsc_template[] = {
  24. 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
  25. 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
  26. 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
  27. 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
  28. 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
  29. 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
  30. 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
  31. 0x12, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x00, 0x7f,
  32. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */
  33. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  34. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
  35. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  36. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
  37. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  38. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
  39. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00,
  40. 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
  41. 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00,
  42. 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */
  43. 0x00, 0x28, 0x44, 0x44, 0xa0, 0x88, 0x5a, 0x01,
  44. 0x08, 0x08, 0x08, 0x08, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */
  45. 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44,
  46. 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */
  47. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  48. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
  49. 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00,
  50. 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */
  51. 0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81,
  52. 0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
  53. 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
  54. 0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
  55. 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00,
  56. };
  57. static const u8 tbl_tw2864_pal_template[] = {
  58. 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */
  59. 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
  60. 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */
  61. 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
  62. 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */
  63. 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
  64. 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */
  65. 0x18, 0xf5, 0x0c, 0xd0, 0x00, 0x00, 0x01, 0x7f,
  66. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */
  67. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  68. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
  69. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  70. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
  71. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  72. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
  73. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00,
  74. 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
  75. 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00,
  76. 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */
  77. 0x00, 0x28, 0x44, 0x44, 0xa0, 0x90, 0x5a, 0x01,
  78. 0x0a, 0x0a, 0x0a, 0x0a, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */
  79. 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44,
  80. 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */
  81. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  82. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
  83. 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00,
  84. 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */
  85. 0x64, 0xa8, 0xec, 0xc1, 0x0f, 0x11, 0x11, 0x81,
  86. 0x00, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
  87. 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
  88. 0x83, 0xb5, 0x09, 0x00, 0xa0, 0x00, 0x01, 0x20, /* 0xf0 */
  89. 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00,
  90. };
  91. static const u8 tbl_tw2865_ntsc_template[] = {
  92. 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */
  93. 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
  94. 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */
  95. 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
  96. 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */
  97. 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
  98. 0x00, 0xf0, 0x70, 0x48, 0x80, 0x80, 0x00, 0x02, /* 0x30 */
  99. 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f,
  100. 0x00, 0x00, 0x90, 0x68, 0x00, 0x38, 0x80, 0x80, /* 0x40 */
  101. 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00,
  102. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
  103. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  104. 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
  105. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43,
  106. 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */
  107. 0xE9, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80,
  108. 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
  109. 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00,
  110. 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */
  111. 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13,
  112. 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1B, 0x1A, /* 0xa0 */
  113. 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44,
  114. 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */
  115. 0xFF, 0xE7, 0xE9, 0xE9, 0xEB, 0xFF, 0xD6, 0xD8,
  116. 0xD8, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
  117. 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80,
  118. 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */
  119. 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
  120. 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
  121. 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
  122. 0x83, 0xB5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */
  123. 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
  124. };
  125. static const u8 tbl_tw2865_pal_template[] = {
  126. 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */
  127. 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
  128. 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */
  129. 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
  130. 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */
  131. 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
  132. 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */
  133. 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f,
  134. 0x00, 0x94, 0x90, 0x48, 0x00, 0x38, 0x7F, 0x80, /* 0x40 */
  135. 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00,
  136. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
  137. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  138. 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
  139. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43,
  140. 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */
  141. 0xEA, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80,
  142. 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */
  143. 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00,
  144. 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */
  145. 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13,
  146. 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1A, 0x1A, /* 0xa0 */
  147. 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44,
  148. 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */
  149. 0xFF, 0xE7, 0xE9, 0xE9, 0xE9, 0xFF, 0xD7, 0xD8,
  150. 0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
  151. 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80,
  152. 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */
  153. 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81,
  154. 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */
  155. 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00,
  156. 0x83, 0xB5, 0x09, 0x00, 0xA0, 0x00, 0x01, 0x20, /* 0xf0 */
  157. 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0,
  158. };
  159. #define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id)))
  160. static u8 tw_readbyte(struct solo_dev *solo_dev, int chip_id, u8 tw6x_off,
  161. u8 tw_off)
  162. {
  163. if (is_tw286x(solo_dev, chip_id))
  164. return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
  165. TW_CHIP_OFFSET_ADDR(chip_id),
  166. tw6x_off);
  167. else
  168. return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
  169. TW_CHIP_OFFSET_ADDR(chip_id),
  170. tw_off);
  171. }
  172. static void tw_writebyte(struct solo_dev *solo_dev, int chip_id,
  173. u8 tw6x_off, u8 tw_off, u8 val)
  174. {
  175. if (is_tw286x(solo_dev, chip_id))
  176. solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
  177. TW_CHIP_OFFSET_ADDR(chip_id),
  178. tw6x_off, val);
  179. else
  180. solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
  181. TW_CHIP_OFFSET_ADDR(chip_id),
  182. tw_off, val);
  183. }
  184. static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off,
  185. u8 val)
  186. {
  187. int i;
  188. for (i = 0; i < 5; i++) {
  189. u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off);
  190. if (rval == val)
  191. return;
  192. solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val);
  193. msleep_interruptible(1);
  194. }
  195. /* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", */
  196. /* addr, off, val); */
  197. }
  198. static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr)
  199. {
  200. u8 tbl_tw2865_common[256];
  201. int i;
  202. if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
  203. memcpy(tbl_tw2865_common, tbl_tw2865_pal_template,
  204. sizeof(tbl_tw2865_common));
  205. else
  206. memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template,
  207. sizeof(tbl_tw2865_common));
  208. /* ALINK Mode */
  209. if (solo_dev->nr_chans == 4) {
  210. tbl_tw2865_common[0xd2] = 0x01;
  211. tbl_tw2865_common[0xcf] = 0x00;
  212. } else if (solo_dev->nr_chans == 8) {
  213. tbl_tw2865_common[0xd2] = 0x02;
  214. if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
  215. tbl_tw2865_common[0xcf] = 0x80;
  216. } else if (solo_dev->nr_chans == 16) {
  217. tbl_tw2865_common[0xd2] = 0x03;
  218. if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
  219. tbl_tw2865_common[0xcf] = 0x83;
  220. else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
  221. tbl_tw2865_common[0xcf] = 0x83;
  222. else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
  223. tbl_tw2865_common[0xcf] = 0x80;
  224. }
  225. for (i = 0; i < 0xff; i++) {
  226. /* Skip read only registers */
  227. switch (i) {
  228. case 0xb8 ... 0xc1:
  229. case 0xc4 ... 0xc7:
  230. case 0xfd:
  231. continue;
  232. }
  233. switch (i & ~0x30) {
  234. case 0x00:
  235. case 0x0c ... 0x0d:
  236. continue;
  237. }
  238. tw_write_and_verify(solo_dev, dev_addr, i,
  239. tbl_tw2865_common[i]);
  240. }
  241. return 0;
  242. }
  243. static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr)
  244. {
  245. u8 tbl_tw2864_common[256];
  246. int i;
  247. if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL)
  248. memcpy(tbl_tw2864_common, tbl_tw2864_pal_template,
  249. sizeof(tbl_tw2864_common));
  250. else
  251. memcpy(tbl_tw2864_common, tbl_tw2864_ntsc_template,
  252. sizeof(tbl_tw2864_common));
  253. if (solo_dev->tw2865 == 0) {
  254. /* IRQ Mode */
  255. if (solo_dev->nr_chans == 4) {
  256. tbl_tw2864_common[0xd2] = 0x01;
  257. tbl_tw2864_common[0xcf] = 0x00;
  258. } else if (solo_dev->nr_chans == 8) {
  259. tbl_tw2864_common[0xd2] = 0x02;
  260. if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
  261. tbl_tw2864_common[0xcf] = 0x43;
  262. else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
  263. tbl_tw2864_common[0xcf] = 0x40;
  264. } else if (solo_dev->nr_chans == 16) {
  265. tbl_tw2864_common[0xd2] = 0x03;
  266. if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
  267. tbl_tw2864_common[0xcf] = 0x43;
  268. else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
  269. tbl_tw2864_common[0xcf] = 0x43;
  270. else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
  271. tbl_tw2864_common[0xcf] = 0x43;
  272. else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
  273. tbl_tw2864_common[0xcf] = 0x40;
  274. }
  275. } else {
  276. /* ALINK Mode. Assumes that the first tw28xx is a
  277. * 2865 and these are in cascade. */
  278. for (i = 0; i <= 4; i++)
  279. tbl_tw2864_common[0x08 | i << 4] = 0x12;
  280. if (solo_dev->nr_chans == 8) {
  281. tbl_tw2864_common[0xd2] = 0x02;
  282. if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
  283. tbl_tw2864_common[0xcf] = 0x80;
  284. } else if (solo_dev->nr_chans == 16) {
  285. tbl_tw2864_common[0xd2] = 0x03;
  286. if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
  287. tbl_tw2864_common[0xcf] = 0x83;
  288. else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
  289. tbl_tw2864_common[0xcf] = 0x83;
  290. else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
  291. tbl_tw2864_common[0xcf] = 0x80;
  292. }
  293. }
  294. for (i = 0; i < 0xff; i++) {
  295. /* Skip read only registers */
  296. switch (i) {
  297. case 0xb8 ... 0xc1:
  298. case 0xfd:
  299. continue;
  300. }
  301. switch (i & ~0x30) {
  302. case 0x00:
  303. case 0x0c:
  304. case 0x0d:
  305. continue;
  306. }
  307. tw_write_and_verify(solo_dev, dev_addr, i,
  308. tbl_tw2864_common[i]);
  309. }
  310. return 0;
  311. }
  312. static int tw2815_setup(struct solo_dev *solo_dev, u8 dev_addr)
  313. {
  314. u8 tbl_ntsc_tw2815_common[] = {
  315. 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80,
  316. 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11,
  317. };
  318. u8 tbl_pal_tw2815_common[] = {
  319. 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80,
  320. 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11,
  321. };
  322. u8 tbl_tw2815_sfr[] = {
  323. 0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */
  324. 0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00,
  325. 0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */
  326. 0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00,
  327. 0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */
  328. 0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88,
  329. 0x88, 0x11, 0x00, 0x88, 0x88, 0x00, /* 0x30 */
  330. };
  331. u8 *tbl_tw2815_common;
  332. int i;
  333. int ch;
  334. tbl_ntsc_tw2815_common[0x06] = 0;
  335. /* Horizontal Delay Control */
  336. tbl_ntsc_tw2815_common[0x02] = DEFAULT_HDELAY_NTSC & 0xff;
  337. tbl_ntsc_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC >> 8);
  338. /* Horizontal Active Control */
  339. tbl_ntsc_tw2815_common[0x03] = DEFAULT_HACTIVE_NTSC & 0xff;
  340. tbl_ntsc_tw2815_common[0x06] |=
  341. ((0x03 & (DEFAULT_HACTIVE_NTSC >> 8)) << 2);
  342. /* Vertical Delay Control */
  343. tbl_ntsc_tw2815_common[0x04] = DEFAULT_VDELAY_NTSC & 0xff;
  344. tbl_ntsc_tw2815_common[0x06] |=
  345. ((0x01 & (DEFAULT_VDELAY_NTSC >> 8)) << 4);
  346. /* Vertical Active Control */
  347. tbl_ntsc_tw2815_common[0x05] = DEFAULT_VACTIVE_NTSC & 0xff;
  348. tbl_ntsc_tw2815_common[0x06] |=
  349. ((0x01 & (DEFAULT_VACTIVE_NTSC >> 8)) << 5);
  350. tbl_pal_tw2815_common[0x06] = 0;
  351. /* Horizontal Delay Control */
  352. tbl_pal_tw2815_common[0x02] = DEFAULT_HDELAY_PAL & 0xff;
  353. tbl_pal_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL >> 8);
  354. /* Horizontal Active Control */
  355. tbl_pal_tw2815_common[0x03] = DEFAULT_HACTIVE_PAL & 0xff;
  356. tbl_pal_tw2815_common[0x06] |=
  357. ((0x03 & (DEFAULT_HACTIVE_PAL >> 8)) << 2);
  358. /* Vertical Delay Control */
  359. tbl_pal_tw2815_common[0x04] = DEFAULT_VDELAY_PAL & 0xff;
  360. tbl_pal_tw2815_common[0x06] |=
  361. ((0x01 & (DEFAULT_VDELAY_PAL >> 8)) << 4);
  362. /* Vertical Active Control */
  363. tbl_pal_tw2815_common[0x05] = DEFAULT_VACTIVE_PAL & 0xff;
  364. tbl_pal_tw2815_common[0x06] |=
  365. ((0x01 & (DEFAULT_VACTIVE_PAL >> 8)) << 5);
  366. tbl_tw2815_common =
  367. (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ?
  368. tbl_ntsc_tw2815_common : tbl_pal_tw2815_common;
  369. /* Dual ITU-R BT.656 format */
  370. tbl_tw2815_common[0x0d] |= 0x04;
  371. /* Audio configuration */
  372. tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6);
  373. if (solo_dev->nr_chans == 4) {
  374. tbl_tw2815_sfr[0x63 - 0x40] |= 1;
  375. tbl_tw2815_sfr[0x62 - 0x40] |= 3 << 6;
  376. } else if (solo_dev->nr_chans == 8) {
  377. tbl_tw2815_sfr[0x63 - 0x40] |= 2;
  378. if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
  379. tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
  380. else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
  381. tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
  382. } else if (solo_dev->nr_chans == 16) {
  383. tbl_tw2815_sfr[0x63 - 0x40] |= 3;
  384. if (dev_addr == TW_CHIP_OFFSET_ADDR(0))
  385. tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6;
  386. else if (dev_addr == TW_CHIP_OFFSET_ADDR(1))
  387. tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
  388. else if (dev_addr == TW_CHIP_OFFSET_ADDR(2))
  389. tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6;
  390. else if (dev_addr == TW_CHIP_OFFSET_ADDR(3))
  391. tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6;
  392. }
  393. /* Output mode of R_ADATM pin (0 mixing, 1 record) */
  394. /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */
  395. /* 8KHz, used to be 16KHz, but changed for remote client compat */
  396. tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 2;
  397. tbl_tw2815_sfr[0x6c - 0x40] |= 0 << 2;
  398. /* Playback of right channel */
  399. tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5;
  400. /* Reserved value (XXX ??) */
  401. tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5;
  402. /* Analog output gain and mix ratio playback on full */
  403. tbl_tw2815_sfr[0x70 - 0x40] |= 0xff;
  404. /* Select playback audio and mute all except */
  405. tbl_tw2815_sfr[0x71 - 0x40] |= 0x10;
  406. tbl_tw2815_sfr[0x6d - 0x40] |= 0x0f;
  407. /* End of audio configuration */
  408. for (ch = 0; ch < 4; ch++) {
  409. tbl_tw2815_common[0x0d] &= ~3;
  410. switch (ch) {
  411. case 0:
  412. tbl_tw2815_common[0x0d] |= 0x21;
  413. break;
  414. case 1:
  415. tbl_tw2815_common[0x0d] |= 0x20;
  416. break;
  417. case 2:
  418. tbl_tw2815_common[0x0d] |= 0x23;
  419. break;
  420. case 3:
  421. tbl_tw2815_common[0x0d] |= 0x22;
  422. break;
  423. }
  424. for (i = 0; i < 0x0f; i++) {
  425. if (i == 0x00)
  426. continue; /* read-only */
  427. solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
  428. dev_addr, (ch * 0x10) + i,
  429. tbl_tw2815_common[i]);
  430. }
  431. }
  432. for (i = 0x40; i < 0x76; i++) {
  433. /* Skip read-only and nop registers */
  434. if (i == 0x40 || i == 0x59 || i == 0x5a ||
  435. i == 0x5d || i == 0x5e || i == 0x5f)
  436. continue;
  437. solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i,
  438. tbl_tw2815_sfr[i - 0x40]);
  439. }
  440. return 0;
  441. }
  442. #define FIRST_ACTIVE_LINE 0x0008
  443. #define LAST_ACTIVE_LINE 0x0102
  444. static void saa712x_write_regs(struct solo_dev *dev, const u8 *vals,
  445. int start, int n)
  446. {
  447. for (; start < n; start++, vals++) {
  448. /* Skip read-only registers */
  449. switch (start) {
  450. /* case 0x00 ... 0x25: */
  451. case 0x2e ... 0x37:
  452. case 0x60:
  453. case 0x7d:
  454. continue;
  455. }
  456. solo_i2c_writebyte(dev, SOLO_I2C_SAA, 0x46, start, *vals);
  457. }
  458. }
  459. #define SAA712x_reg7c (0x80 | ((LAST_ACTIVE_LINE & 0x100) >> 2) \
  460. | ((FIRST_ACTIVE_LINE & 0x100) >> 4))
  461. static void saa712x_setup(struct solo_dev *dev)
  462. {
  463. const int reg_start = 0x26;
  464. static const u8 saa7128_regs_ntsc[] = {
  465. /* :0x26 */
  466. 0x0d, 0x00,
  467. /* :0x28 */
  468. 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f,
  469. /* :0x2e XXX: read-only */
  470. 0x00, 0x00,
  471. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  472. /* :0x38 */
  473. 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
  474. /* :0x40 */
  475. 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
  476. 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
  477. /* :0x50 */
  478. 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
  479. 0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e,
  480. /* :0x60 */
  481. 0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77,
  482. 0x41, 0x88, 0x41, 0x52, 0xed, 0x10, 0x10, 0x00,
  483. /* :0x70 */
  484. 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
  485. 0x00, 0x00, FIRST_ACTIVE_LINE, LAST_ACTIVE_LINE & 0xff,
  486. SAA712x_reg7c, 0x00, 0xff, 0xff,
  487. }, saa7128_regs_pal[] = {
  488. /* :0x26 */
  489. 0x0d, 0x00,
  490. /* :0x28 */
  491. 0xe1, 0x1d, 0x75, 0x3f, 0x06, 0x3f,
  492. /* :0x2e XXX: read-only */
  493. 0x00, 0x00,
  494. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  495. /* :0x38 */
  496. 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
  497. /* :0x40 */
  498. 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
  499. 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
  500. /* :0x50 */
  501. 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
  502. 0x02, 0x80, 0x0f, 0x77, 0xa7, 0x67, 0x66, 0x2e,
  503. /* :0x60 */
  504. 0x7b, 0x02, 0x35, 0xcb, 0x8a, 0x09, 0x2a, 0x77,
  505. 0x41, 0x88, 0x41, 0x52, 0xf1, 0x10, 0x20, 0x00,
  506. /* :0x70 */
  507. 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
  508. 0x00, 0x00, 0x12, 0x30,
  509. SAA712x_reg7c | 0x40, 0x00, 0xff, 0xff,
  510. };
  511. if (dev->video_type == SOLO_VO_FMT_TYPE_PAL)
  512. saa712x_write_regs(dev, saa7128_regs_pal, reg_start,
  513. sizeof(saa7128_regs_pal));
  514. else
  515. saa712x_write_regs(dev, saa7128_regs_ntsc, reg_start,
  516. sizeof(saa7128_regs_ntsc));
  517. }
  518. int solo_tw28_init(struct solo_dev *solo_dev)
  519. {
  520. int i;
  521. u8 value;
  522. solo_dev->tw28_cnt = 0;
  523. /* Detect techwell chip type(s) */
  524. for (i = 0; i < solo_dev->nr_chans / 4; i++) {
  525. value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
  526. TW_CHIP_OFFSET_ADDR(i), 0xFF);
  527. switch (value >> 3) {
  528. case 0x18:
  529. solo_dev->tw2865 |= 1 << i;
  530. solo_dev->tw28_cnt++;
  531. break;
  532. case 0x0c:
  533. case 0x0d:
  534. solo_dev->tw2864 |= 1 << i;
  535. solo_dev->tw28_cnt++;
  536. break;
  537. default:
  538. value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
  539. TW_CHIP_OFFSET_ADDR(i),
  540. 0x59);
  541. if ((value >> 3) == 0x04) {
  542. solo_dev->tw2815 |= 1 << i;
  543. solo_dev->tw28_cnt++;
  544. }
  545. }
  546. }
  547. if (solo_dev->tw28_cnt != (solo_dev->nr_chans >> 2)) {
  548. dev_err(&solo_dev->pdev->dev,
  549. "Could not initialize any techwell chips\n");
  550. return -EINVAL;
  551. }
  552. saa712x_setup(solo_dev);
  553. for (i = 0; i < solo_dev->tw28_cnt; i++) {
  554. if ((solo_dev->tw2865 & (1 << i)))
  555. tw2865_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
  556. else if ((solo_dev->tw2864 & (1 << i)))
  557. tw2864_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
  558. else
  559. tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i));
  560. }
  561. return 0;
  562. }
  563. /*
  564. * We accessed the video status signal in the Techwell chip through
  565. * iic/i2c because the video status reported by register REG_VI_STATUS1
  566. * (address 0x012C) of the SOLO6010 chip doesn't give the correct video
  567. * status signal values.
  568. */
  569. int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch)
  570. {
  571. u8 val, chip_num;
  572. /* Get the right chip and on-chip channel */
  573. chip_num = ch / 4;
  574. ch %= 4;
  575. val = tw_readbyte(solo_dev, chip_num, TW286x_AV_STAT_ADDR,
  576. TW_AV_STAT_ADDR) & 0x0f;
  577. return val & (1 << ch) ? 1 : 0;
  578. }
  579. #if 0
  580. /* Status of audio from up to 4 techwell chips are combined into 1 variable.
  581. * See techwell datasheet for details. */
  582. u16 tw28_get_audio_status(struct solo_dev *solo_dev)
  583. {
  584. u8 val;
  585. u16 status = 0;
  586. int i;
  587. for (i = 0; i < solo_dev->tw28_cnt; i++) {
  588. val = (tw_readbyte(solo_dev, i, TW286x_AV_STAT_ADDR,
  589. TW_AV_STAT_ADDR) & 0xf0) >> 4;
  590. status |= val << (i * 4);
  591. }
  592. return status;
  593. }
  594. #endif
  595. bool tw28_has_sharpness(struct solo_dev *solo_dev, u8 ch)
  596. {
  597. return is_tw286x(solo_dev, ch / 4);
  598. }
  599. int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
  600. s32 val)
  601. {
  602. char sval;
  603. u8 chip_num;
  604. /* Get the right chip and on-chip channel */
  605. chip_num = ch / 4;
  606. ch %= 4;
  607. if (val > 255 || val < 0)
  608. return -ERANGE;
  609. switch (ctrl) {
  610. case V4L2_CID_SHARPNESS:
  611. /* Only 286x has sharpness */
  612. if (is_tw286x(solo_dev, chip_num)) {
  613. u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
  614. TW_CHIP_OFFSET_ADDR(chip_num),
  615. TW286x_SHARPNESS(chip_num));
  616. v &= 0xf0;
  617. v |= val;
  618. solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
  619. TW_CHIP_OFFSET_ADDR(chip_num),
  620. TW286x_SHARPNESS(chip_num), v);
  621. } else {
  622. return -EINVAL;
  623. }
  624. break;
  625. case V4L2_CID_HUE:
  626. if (is_tw286x(solo_dev, chip_num))
  627. sval = val - 128;
  628. else
  629. sval = (char)val;
  630. tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
  631. TW_HUE_ADDR(ch), sval);
  632. break;
  633. case V4L2_CID_SATURATION:
  634. /* 286x chips have a U and V component for saturation */
  635. if (is_tw286x(solo_dev, chip_num)) {
  636. solo_i2c_writebyte(solo_dev, SOLO_I2C_TW,
  637. TW_CHIP_OFFSET_ADDR(chip_num),
  638. TW286x_SATURATIONU_ADDR(ch), val);
  639. }
  640. tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch),
  641. TW_SATURATION_ADDR(ch), val);
  642. break;
  643. case V4L2_CID_CONTRAST:
  644. tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch),
  645. TW_CONTRAST_ADDR(ch), val);
  646. break;
  647. case V4L2_CID_BRIGHTNESS:
  648. if (is_tw286x(solo_dev, chip_num))
  649. sval = val - 128;
  650. else
  651. sval = (char)val;
  652. tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch),
  653. TW_BRIGHTNESS_ADDR(ch), sval);
  654. break;
  655. default:
  656. return -EINVAL;
  657. }
  658. return 0;
  659. }
  660. int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch,
  661. s32 *val)
  662. {
  663. u8 rval, chip_num;
  664. /* Get the right chip and on-chip channel */
  665. chip_num = ch / 4;
  666. ch %= 4;
  667. switch (ctrl) {
  668. case V4L2_CID_SHARPNESS:
  669. /* Only 286x has sharpness */
  670. if (is_tw286x(solo_dev, chip_num)) {
  671. rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW,
  672. TW_CHIP_OFFSET_ADDR(chip_num),
  673. TW286x_SHARPNESS(chip_num));
  674. *val = rval & 0x0f;
  675. } else
  676. *val = 0;
  677. break;
  678. case V4L2_CID_HUE:
  679. rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch),
  680. TW_HUE_ADDR(ch));
  681. if (is_tw286x(solo_dev, chip_num))
  682. *val = (s32)((char)rval) + 128;
  683. else
  684. *val = rval;
  685. break;
  686. case V4L2_CID_SATURATION:
  687. *val = tw_readbyte(solo_dev, chip_num,
  688. TW286x_SATURATIONU_ADDR(ch),
  689. TW_SATURATION_ADDR(ch));
  690. break;
  691. case V4L2_CID_CONTRAST:
  692. *val = tw_readbyte(solo_dev, chip_num,
  693. TW286x_CONTRAST_ADDR(ch),
  694. TW_CONTRAST_ADDR(ch));
  695. break;
  696. case V4L2_CID_BRIGHTNESS:
  697. rval = tw_readbyte(solo_dev, chip_num,
  698. TW286x_BRIGHTNESS_ADDR(ch),
  699. TW_BRIGHTNESS_ADDR(ch));
  700. if (is_tw286x(solo_dev, chip_num))
  701. *val = (s32)((char)rval) + 128;
  702. else
  703. *val = rval;
  704. break;
  705. default:
  706. return -EINVAL;
  707. }
  708. return 0;
  709. }
  710. #if 0
  711. /*
  712. * For audio output volume, the output channel is only 1. In this case we
  713. * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used
  714. * is the base address of the techwell chip.
  715. */
  716. void tw2815_Set_AudioOutVol(struct solo_dev *solo_dev, unsigned int u_val)
  717. {
  718. unsigned int val;
  719. unsigned int chip_num;
  720. chip_num = (solo_dev->nr_chans - 1) / 4;
  721. val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
  722. TW_AUDIO_OUTPUT_VOL_ADDR);
  723. u_val = (val & 0x0f) | (u_val << 4);
  724. tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR,
  725. TW_AUDIO_OUTPUT_VOL_ADDR, u_val);
  726. }
  727. #endif
  728. u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch)
  729. {
  730. u8 val;
  731. u8 chip_num;
  732. /* Get the right chip and on-chip channel */
  733. chip_num = ch / 4;
  734. ch %= 4;
  735. val = tw_readbyte(solo_dev, chip_num,
  736. TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
  737. TW_AUDIO_INPUT_GAIN_ADDR(ch));
  738. return (ch % 2) ? (val >> 4) : (val & 0x0f);
  739. }
  740. void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val)
  741. {
  742. u8 old_val;
  743. u8 chip_num;
  744. /* Get the right chip and on-chip channel */
  745. chip_num = ch / 4;
  746. ch %= 4;
  747. old_val = tw_readbyte(solo_dev, chip_num,
  748. TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
  749. TW_AUDIO_INPUT_GAIN_ADDR(ch));
  750. val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) |
  751. ((ch % 2) ? (val << 4) : val);
  752. tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch),
  753. TW_AUDIO_INPUT_GAIN_ADDR(ch), val);
  754. }