si476x-prop.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /*
  2. * drivers/mfd/si476x-prop.c -- Subroutines to access
  3. * properties of si476x chips
  4. *
  5. * Copyright (C) 2012 Innovative Converged Devices(ICD)
  6. * Copyright (C) 2013 Andrey Smirnov
  7. *
  8. * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; version 2 of the License.
  13. *
  14. * This program is distributed in the hope that it will be useful, but
  15. * WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * General Public License for more details.
  18. */
  19. #include <linux/module.h>
  20. #include <media/si476x.h>
  21. #include <linux/mfd/si476x-core.h>
  22. struct si476x_property_range {
  23. u16 low, high;
  24. };
  25. static bool si476x_core_element_is_in_array(u16 element,
  26. const u16 array[],
  27. size_t size)
  28. {
  29. int i;
  30. for (i = 0; i < size; i++)
  31. if (element == array[i])
  32. return true;
  33. return false;
  34. }
  35. static bool si476x_core_element_is_in_range(u16 element,
  36. const struct si476x_property_range range[],
  37. size_t size)
  38. {
  39. int i;
  40. for (i = 0; i < size; i++)
  41. if (element <= range[i].high && element >= range[i].low)
  42. return true;
  43. return false;
  44. }
  45. static bool si476x_core_is_valid_property_a10(struct si476x_core *core,
  46. u16 property)
  47. {
  48. static const u16 valid_properties[] = {
  49. 0x0000,
  50. 0x0500, 0x0501,
  51. 0x0600,
  52. 0x0709, 0x070C, 0x070D, 0x70E, 0x710,
  53. 0x0718,
  54. 0x1207, 0x1208,
  55. 0x2007,
  56. 0x2300,
  57. };
  58. static const struct si476x_property_range valid_ranges[] = {
  59. { 0x0200, 0x0203 },
  60. { 0x0300, 0x0303 },
  61. { 0x0400, 0x0404 },
  62. { 0x0700, 0x0707 },
  63. { 0x1100, 0x1102 },
  64. { 0x1200, 0x1204 },
  65. { 0x1300, 0x1306 },
  66. { 0x2000, 0x2005 },
  67. { 0x2100, 0x2104 },
  68. { 0x2106, 0x2106 },
  69. { 0x2200, 0x220E },
  70. { 0x3100, 0x3104 },
  71. { 0x3207, 0x320F },
  72. { 0x3300, 0x3304 },
  73. { 0x3500, 0x3517 },
  74. { 0x3600, 0x3617 },
  75. { 0x3700, 0x3717 },
  76. { 0x4000, 0x4003 },
  77. };
  78. return si476x_core_element_is_in_range(property, valid_ranges,
  79. ARRAY_SIZE(valid_ranges)) ||
  80. si476x_core_element_is_in_array(property, valid_properties,
  81. ARRAY_SIZE(valid_properties));
  82. }
  83. static bool si476x_core_is_valid_property_a20(struct si476x_core *core,
  84. u16 property)
  85. {
  86. static const u16 valid_properties[] = {
  87. 0x071B,
  88. 0x1006,
  89. 0x2210,
  90. 0x3401,
  91. };
  92. static const struct si476x_property_range valid_ranges[] = {
  93. { 0x2215, 0x2219 },
  94. };
  95. return si476x_core_is_valid_property_a10(core, property) ||
  96. si476x_core_element_is_in_range(property, valid_ranges,
  97. ARRAY_SIZE(valid_ranges)) ||
  98. si476x_core_element_is_in_array(property, valid_properties,
  99. ARRAY_SIZE(valid_properties));
  100. }
  101. static bool si476x_core_is_valid_property_a30(struct si476x_core *core,
  102. u16 property)
  103. {
  104. static const u16 valid_properties[] = {
  105. 0x071C, 0x071D,
  106. 0x1007, 0x1008,
  107. 0x220F, 0x2214,
  108. 0x2301,
  109. 0x3105, 0x3106,
  110. 0x3402,
  111. };
  112. static const struct si476x_property_range valid_ranges[] = {
  113. { 0x0405, 0x0411 },
  114. { 0x2008, 0x200B },
  115. { 0x2220, 0x2223 },
  116. { 0x3100, 0x3106 },
  117. };
  118. return si476x_core_is_valid_property_a20(core, property) ||
  119. si476x_core_element_is_in_range(property, valid_ranges,
  120. ARRAY_SIZE(valid_ranges)) ||
  121. si476x_core_element_is_in_array(property, valid_properties,
  122. ARRAY_SIZE(valid_properties));
  123. }
  124. typedef bool (*valid_property_pred_t) (struct si476x_core *, u16);
  125. static bool si476x_core_is_valid_property(struct si476x_core *core,
  126. u16 property)
  127. {
  128. static const valid_property_pred_t is_valid_property[] = {
  129. [SI476X_REVISION_A10] = si476x_core_is_valid_property_a10,
  130. [SI476X_REVISION_A20] = si476x_core_is_valid_property_a20,
  131. [SI476X_REVISION_A30] = si476x_core_is_valid_property_a30,
  132. };
  133. BUG_ON(core->revision > SI476X_REVISION_A30 ||
  134. core->revision == -1);
  135. return is_valid_property[core->revision](core, property);
  136. }
  137. static bool si476x_core_is_readonly_property(struct si476x_core *core,
  138. u16 property)
  139. {
  140. BUG_ON(core->revision > SI476X_REVISION_A30 ||
  141. core->revision == -1);
  142. switch (core->revision) {
  143. case SI476X_REVISION_A10:
  144. return (property == 0x3200);
  145. case SI476X_REVISION_A20:
  146. return (property == 0x1006 ||
  147. property == 0x2210 ||
  148. property == 0x3200);
  149. case SI476X_REVISION_A30:
  150. return false;
  151. }
  152. return false;
  153. }
  154. static bool si476x_core_regmap_readable_register(struct device *dev,
  155. unsigned int reg)
  156. {
  157. struct i2c_client *client = to_i2c_client(dev);
  158. struct si476x_core *core = i2c_get_clientdata(client);
  159. return si476x_core_is_valid_property(core, (u16) reg);
  160. }
  161. static bool si476x_core_regmap_writable_register(struct device *dev,
  162. unsigned int reg)
  163. {
  164. struct i2c_client *client = to_i2c_client(dev);
  165. struct si476x_core *core = i2c_get_clientdata(client);
  166. return si476x_core_is_valid_property(core, (u16) reg) &&
  167. !si476x_core_is_readonly_property(core, (u16) reg);
  168. }
  169. static int si476x_core_regmap_write(void *context, unsigned int reg,
  170. unsigned int val)
  171. {
  172. return si476x_core_cmd_set_property(context, reg, val);
  173. }
  174. static int si476x_core_regmap_read(void *context, unsigned int reg,
  175. unsigned *val)
  176. {
  177. struct si476x_core *core = context;
  178. int err;
  179. err = si476x_core_cmd_get_property(core, reg);
  180. if (err < 0)
  181. return err;
  182. *val = err;
  183. return 0;
  184. }
  185. static const struct regmap_config si476x_regmap_config = {
  186. .reg_bits = 16,
  187. .val_bits = 16,
  188. .max_register = 0x4003,
  189. .writeable_reg = si476x_core_regmap_writable_register,
  190. .readable_reg = si476x_core_regmap_readable_register,
  191. .reg_read = si476x_core_regmap_read,
  192. .reg_write = si476x_core_regmap_write,
  193. .cache_type = REGCACHE_RBTREE,
  194. };
  195. struct regmap *devm_regmap_init_si476x(struct si476x_core *core)
  196. {
  197. return devm_regmap_init(&core->client->dev, NULL,
  198. core, &si476x_regmap_config);
  199. }
  200. EXPORT_SYMBOL_GPL(devm_regmap_init_si476x);