ssm4567.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. /*
  2. * SSM4567 amplifier audio driver
  3. *
  4. * Copyright 2014 Google Chromium project.
  5. * Author: Anatol Pomozov <anatol@chromium.org>
  6. *
  7. * Based on code copyright/by:
  8. * Copyright 2013 Analog Devices Inc.
  9. *
  10. * Licensed under the GPL-2.
  11. */
  12. #include <linux/acpi.h>
  13. #include <linux/module.h>
  14. #include <linux/init.h>
  15. #include <linux/i2c.h>
  16. #include <linux/regmap.h>
  17. #include <linux/slab.h>
  18. #include <sound/core.h>
  19. #include <sound/pcm.h>
  20. #include <sound/pcm_params.h>
  21. #include <sound/soc.h>
  22. #include <sound/initval.h>
  23. #include <sound/tlv.h>
  24. #define SSM4567_REG_POWER_CTRL 0x00
  25. #define SSM4567_REG_AMP_SNS_CTRL 0x01
  26. #define SSM4567_REG_DAC_CTRL 0x02
  27. #define SSM4567_REG_DAC_VOLUME 0x03
  28. #define SSM4567_REG_SAI_CTRL_1 0x04
  29. #define SSM4567_REG_SAI_CTRL_2 0x05
  30. #define SSM4567_REG_SAI_PLACEMENT_1 0x06
  31. #define SSM4567_REG_SAI_PLACEMENT_2 0x07
  32. #define SSM4567_REG_SAI_PLACEMENT_3 0x08
  33. #define SSM4567_REG_SAI_PLACEMENT_4 0x09
  34. #define SSM4567_REG_SAI_PLACEMENT_5 0x0a
  35. #define SSM4567_REG_SAI_PLACEMENT_6 0x0b
  36. #define SSM4567_REG_BATTERY_V_OUT 0x0c
  37. #define SSM4567_REG_LIMITER_CTRL_1 0x0d
  38. #define SSM4567_REG_LIMITER_CTRL_2 0x0e
  39. #define SSM4567_REG_LIMITER_CTRL_3 0x0f
  40. #define SSM4567_REG_STATUS_1 0x10
  41. #define SSM4567_REG_STATUS_2 0x11
  42. #define SSM4567_REG_FAULT_CTRL 0x12
  43. #define SSM4567_REG_PDM_CTRL 0x13
  44. #define SSM4567_REG_MCLK_RATIO 0x14
  45. #define SSM4567_REG_BOOST_CTRL_1 0x15
  46. #define SSM4567_REG_BOOST_CTRL_2 0x16
  47. #define SSM4567_REG_SOFT_RESET 0xff
  48. /* POWER_CTRL */
  49. #define SSM4567_POWER_APWDN_EN BIT(7)
  50. #define SSM4567_POWER_BSNS_PWDN BIT(6)
  51. #define SSM4567_POWER_VSNS_PWDN BIT(5)
  52. #define SSM4567_POWER_ISNS_PWDN BIT(4)
  53. #define SSM4567_POWER_BOOST_PWDN BIT(3)
  54. #define SSM4567_POWER_AMP_PWDN BIT(2)
  55. #define SSM4567_POWER_VBAT_ONLY BIT(1)
  56. #define SSM4567_POWER_SPWDN BIT(0)
  57. /* DAC_CTRL */
  58. #define SSM4567_DAC_HV BIT(7)
  59. #define SSM4567_DAC_MUTE BIT(6)
  60. #define SSM4567_DAC_HPF BIT(5)
  61. #define SSM4567_DAC_LPM BIT(4)
  62. #define SSM4567_DAC_FS_MASK 0x7
  63. #define SSM4567_DAC_FS_8000_12000 0x0
  64. #define SSM4567_DAC_FS_16000_24000 0x1
  65. #define SSM4567_DAC_FS_32000_48000 0x2
  66. #define SSM4567_DAC_FS_64000_96000 0x3
  67. #define SSM4567_DAC_FS_128000_192000 0x4
  68. /* SAI_CTRL_1 */
  69. #define SSM4567_SAI_CTRL_1_BCLK BIT(6)
  70. #define SSM4567_SAI_CTRL_1_TDM_BLCKS_MASK (0x3 << 4)
  71. #define SSM4567_SAI_CTRL_1_TDM_BLCKS_32 (0x0 << 4)
  72. #define SSM4567_SAI_CTRL_1_TDM_BLCKS_48 (0x1 << 4)
  73. #define SSM4567_SAI_CTRL_1_TDM_BLCKS_64 (0x2 << 4)
  74. #define SSM4567_SAI_CTRL_1_FSYNC BIT(3)
  75. #define SSM4567_SAI_CTRL_1_LJ BIT(2)
  76. #define SSM4567_SAI_CTRL_1_TDM BIT(1)
  77. #define SSM4567_SAI_CTRL_1_PDM BIT(0)
  78. /* SAI_CTRL_2 */
  79. #define SSM4567_SAI_CTRL_2_AUTO_SLOT BIT(3)
  80. #define SSM4567_SAI_CTRL_2_TDM_SLOT_MASK 0x7
  81. #define SSM4567_SAI_CTRL_2_TDM_SLOT(x) (x)
  82. struct ssm4567 {
  83. struct regmap *regmap;
  84. };
  85. static const struct reg_default ssm4567_reg_defaults[] = {
  86. { SSM4567_REG_POWER_CTRL, 0x81 },
  87. { SSM4567_REG_AMP_SNS_CTRL, 0x09 },
  88. { SSM4567_REG_DAC_CTRL, 0x32 },
  89. { SSM4567_REG_DAC_VOLUME, 0x40 },
  90. { SSM4567_REG_SAI_CTRL_1, 0x00 },
  91. { SSM4567_REG_SAI_CTRL_2, 0x08 },
  92. { SSM4567_REG_SAI_PLACEMENT_1, 0x01 },
  93. { SSM4567_REG_SAI_PLACEMENT_2, 0x20 },
  94. { SSM4567_REG_SAI_PLACEMENT_3, 0x32 },
  95. { SSM4567_REG_SAI_PLACEMENT_4, 0x07 },
  96. { SSM4567_REG_SAI_PLACEMENT_5, 0x07 },
  97. { SSM4567_REG_SAI_PLACEMENT_6, 0x07 },
  98. { SSM4567_REG_BATTERY_V_OUT, 0x00 },
  99. { SSM4567_REG_LIMITER_CTRL_1, 0xa4 },
  100. { SSM4567_REG_LIMITER_CTRL_2, 0x73 },
  101. { SSM4567_REG_LIMITER_CTRL_3, 0x00 },
  102. { SSM4567_REG_STATUS_1, 0x00 },
  103. { SSM4567_REG_STATUS_2, 0x00 },
  104. { SSM4567_REG_FAULT_CTRL, 0x30 },
  105. { SSM4567_REG_PDM_CTRL, 0x40 },
  106. { SSM4567_REG_MCLK_RATIO, 0x11 },
  107. { SSM4567_REG_BOOST_CTRL_1, 0x03 },
  108. { SSM4567_REG_BOOST_CTRL_2, 0x00 },
  109. { SSM4567_REG_SOFT_RESET, 0x00 },
  110. };
  111. static bool ssm4567_readable_reg(struct device *dev, unsigned int reg)
  112. {
  113. switch (reg) {
  114. case SSM4567_REG_POWER_CTRL ... SSM4567_REG_BOOST_CTRL_2:
  115. return true;
  116. default:
  117. return false;
  118. }
  119. }
  120. static bool ssm4567_writeable_reg(struct device *dev, unsigned int reg)
  121. {
  122. switch (reg) {
  123. case SSM4567_REG_POWER_CTRL ... SSM4567_REG_SAI_PLACEMENT_6:
  124. case SSM4567_REG_LIMITER_CTRL_1 ... SSM4567_REG_LIMITER_CTRL_3:
  125. case SSM4567_REG_FAULT_CTRL ... SSM4567_REG_BOOST_CTRL_2:
  126. /* The datasheet states that soft reset register is read-only,
  127. * but logically it is write-only. */
  128. case SSM4567_REG_SOFT_RESET:
  129. return true;
  130. default:
  131. return false;
  132. }
  133. }
  134. static bool ssm4567_volatile_reg(struct device *dev, unsigned int reg)
  135. {
  136. switch (reg) {
  137. case SSM4567_REG_BATTERY_V_OUT:
  138. case SSM4567_REG_STATUS_1 ... SSM4567_REG_STATUS_2:
  139. case SSM4567_REG_SOFT_RESET:
  140. return true;
  141. default:
  142. return false;
  143. }
  144. }
  145. static const DECLARE_TLV_DB_MINMAX_MUTE(ssm4567_vol_tlv, -7125, 2400);
  146. static const struct snd_kcontrol_new ssm4567_snd_controls[] = {
  147. SOC_SINGLE_TLV("Master Playback Volume", SSM4567_REG_DAC_VOLUME, 0,
  148. 0xff, 1, ssm4567_vol_tlv),
  149. SOC_SINGLE("DAC Low Power Mode Switch", SSM4567_REG_DAC_CTRL, 4, 1, 0),
  150. SOC_SINGLE("DAC High Pass Filter Switch", SSM4567_REG_DAC_CTRL,
  151. 5, 1, 0),
  152. };
  153. static const struct snd_kcontrol_new ssm4567_amplifier_boost_control =
  154. SOC_DAPM_SINGLE("Switch", SSM4567_REG_POWER_CTRL, 1, 1, 1);
  155. static const struct snd_soc_dapm_widget ssm4567_dapm_widgets[] = {
  156. SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM4567_REG_POWER_CTRL, 2, 1),
  157. SND_SOC_DAPM_SWITCH("Amplifier Boost", SSM4567_REG_POWER_CTRL, 3, 1,
  158. &ssm4567_amplifier_boost_control),
  159. SND_SOC_DAPM_SIGGEN("Sense"),
  160. SND_SOC_DAPM_PGA("Current Sense", SSM4567_REG_POWER_CTRL, 4, 1, NULL, 0),
  161. SND_SOC_DAPM_PGA("Voltage Sense", SSM4567_REG_POWER_CTRL, 5, 1, NULL, 0),
  162. SND_SOC_DAPM_PGA("VBAT Sense", SSM4567_REG_POWER_CTRL, 6, 1, NULL, 0),
  163. SND_SOC_DAPM_OUTPUT("OUT"),
  164. };
  165. static const struct snd_soc_dapm_route ssm4567_routes[] = {
  166. { "OUT", NULL, "Amplifier Boost" },
  167. { "Amplifier Boost", "Switch", "DAC" },
  168. { "OUT", NULL, "DAC" },
  169. { "Current Sense", NULL, "Sense" },
  170. { "Voltage Sense", NULL, "Sense" },
  171. { "VBAT Sense", NULL, "Sense" },
  172. { "Capture Sense", NULL, "Current Sense" },
  173. { "Capture Sense", NULL, "Voltage Sense" },
  174. { "Capture Sense", NULL, "VBAT Sense" },
  175. };
  176. static int ssm4567_hw_params(struct snd_pcm_substream *substream,
  177. struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
  178. {
  179. struct snd_soc_codec *codec = dai->codec;
  180. struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(codec);
  181. unsigned int rate = params_rate(params);
  182. unsigned int dacfs;
  183. if (rate >= 8000 && rate <= 12000)
  184. dacfs = SSM4567_DAC_FS_8000_12000;
  185. else if (rate >= 16000 && rate <= 24000)
  186. dacfs = SSM4567_DAC_FS_16000_24000;
  187. else if (rate >= 32000 && rate <= 48000)
  188. dacfs = SSM4567_DAC_FS_32000_48000;
  189. else if (rate >= 64000 && rate <= 96000)
  190. dacfs = SSM4567_DAC_FS_64000_96000;
  191. else if (rate >= 128000 && rate <= 192000)
  192. dacfs = SSM4567_DAC_FS_128000_192000;
  193. else
  194. return -EINVAL;
  195. return regmap_update_bits(ssm4567->regmap, SSM4567_REG_DAC_CTRL,
  196. SSM4567_DAC_FS_MASK, dacfs);
  197. }
  198. static int ssm4567_mute(struct snd_soc_dai *dai, int mute)
  199. {
  200. struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(dai->codec);
  201. unsigned int val;
  202. val = mute ? SSM4567_DAC_MUTE : 0;
  203. return regmap_update_bits(ssm4567->regmap, SSM4567_REG_DAC_CTRL,
  204. SSM4567_DAC_MUTE, val);
  205. }
  206. static int ssm4567_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
  207. unsigned int rx_mask, int slots, int width)
  208. {
  209. struct ssm4567 *ssm4567 = snd_soc_dai_get_drvdata(dai);
  210. unsigned int blcks;
  211. int slot;
  212. int ret;
  213. if (tx_mask == 0)
  214. return -EINVAL;
  215. if (rx_mask && rx_mask != tx_mask)
  216. return -EINVAL;
  217. slot = __ffs(tx_mask);
  218. if (tx_mask != BIT(slot))
  219. return -EINVAL;
  220. switch (width) {
  221. case 32:
  222. blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_32;
  223. break;
  224. case 48:
  225. blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_48;
  226. break;
  227. case 64:
  228. blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_64;
  229. break;
  230. default:
  231. return -EINVAL;
  232. }
  233. ret = regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_2,
  234. SSM4567_SAI_CTRL_2_AUTO_SLOT | SSM4567_SAI_CTRL_2_TDM_SLOT_MASK,
  235. SSM4567_SAI_CTRL_2_TDM_SLOT(slot));
  236. if (ret)
  237. return ret;
  238. return regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_1,
  239. SSM4567_SAI_CTRL_1_TDM_BLCKS_MASK, blcks);
  240. }
  241. static int ssm4567_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
  242. {
  243. struct ssm4567 *ssm4567 = snd_soc_dai_get_drvdata(dai);
  244. unsigned int ctrl1 = 0;
  245. bool invert_fclk;
  246. switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
  247. case SND_SOC_DAIFMT_CBS_CFS:
  248. break;
  249. default:
  250. return -EINVAL;
  251. }
  252. switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
  253. case SND_SOC_DAIFMT_NB_NF:
  254. invert_fclk = false;
  255. break;
  256. case SND_SOC_DAIFMT_IB_NF:
  257. ctrl1 |= SSM4567_SAI_CTRL_1_BCLK;
  258. invert_fclk = false;
  259. break;
  260. case SND_SOC_DAIFMT_NB_IF:
  261. ctrl1 |= SSM4567_SAI_CTRL_1_FSYNC;
  262. invert_fclk = true;
  263. break;
  264. case SND_SOC_DAIFMT_IB_IF:
  265. ctrl1 |= SSM4567_SAI_CTRL_1_BCLK;
  266. invert_fclk = true;
  267. break;
  268. default:
  269. return -EINVAL;
  270. }
  271. switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
  272. case SND_SOC_DAIFMT_I2S:
  273. break;
  274. case SND_SOC_DAIFMT_LEFT_J:
  275. ctrl1 |= SSM4567_SAI_CTRL_1_LJ;
  276. invert_fclk = !invert_fclk;
  277. break;
  278. case SND_SOC_DAIFMT_DSP_A:
  279. ctrl1 |= SSM4567_SAI_CTRL_1_TDM;
  280. break;
  281. case SND_SOC_DAIFMT_DSP_B:
  282. ctrl1 |= SSM4567_SAI_CTRL_1_TDM | SSM4567_SAI_CTRL_1_LJ;
  283. break;
  284. case SND_SOC_DAIFMT_PDM:
  285. ctrl1 |= SSM4567_SAI_CTRL_1_PDM;
  286. break;
  287. default:
  288. return -EINVAL;
  289. }
  290. if (invert_fclk)
  291. ctrl1 |= SSM4567_SAI_CTRL_1_FSYNC;
  292. return regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_1,
  293. SSM4567_SAI_CTRL_1_BCLK |
  294. SSM4567_SAI_CTRL_1_FSYNC |
  295. SSM4567_SAI_CTRL_1_LJ |
  296. SSM4567_SAI_CTRL_1_TDM |
  297. SSM4567_SAI_CTRL_1_PDM,
  298. ctrl1);
  299. }
  300. static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable)
  301. {
  302. int ret = 0;
  303. if (!enable) {
  304. ret = regmap_update_bits(ssm4567->regmap,
  305. SSM4567_REG_POWER_CTRL,
  306. SSM4567_POWER_SPWDN, SSM4567_POWER_SPWDN);
  307. regcache_mark_dirty(ssm4567->regmap);
  308. }
  309. regcache_cache_only(ssm4567->regmap, !enable);
  310. if (enable) {
  311. ret = regmap_write(ssm4567->regmap, SSM4567_REG_SOFT_RESET,
  312. 0x00);
  313. if (ret)
  314. return ret;
  315. ret = regmap_update_bits(ssm4567->regmap,
  316. SSM4567_REG_POWER_CTRL,
  317. SSM4567_POWER_SPWDN, 0x00);
  318. regcache_sync(ssm4567->regmap);
  319. }
  320. return ret;
  321. }
  322. static int ssm4567_set_bias_level(struct snd_soc_codec *codec,
  323. enum snd_soc_bias_level level)
  324. {
  325. struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(codec);
  326. int ret = 0;
  327. switch (level) {
  328. case SND_SOC_BIAS_ON:
  329. break;
  330. case SND_SOC_BIAS_PREPARE:
  331. break;
  332. case SND_SOC_BIAS_STANDBY:
  333. if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
  334. ret = ssm4567_set_power(ssm4567, true);
  335. break;
  336. case SND_SOC_BIAS_OFF:
  337. ret = ssm4567_set_power(ssm4567, false);
  338. break;
  339. }
  340. return ret;
  341. }
  342. static const struct snd_soc_dai_ops ssm4567_dai_ops = {
  343. .hw_params = ssm4567_hw_params,
  344. .digital_mute = ssm4567_mute,
  345. .set_fmt = ssm4567_set_dai_fmt,
  346. .set_tdm_slot = ssm4567_set_tdm_slot,
  347. };
  348. static struct snd_soc_dai_driver ssm4567_dai = {
  349. .name = "ssm4567-hifi",
  350. .playback = {
  351. .stream_name = "Playback",
  352. .channels_min = 1,
  353. .channels_max = 1,
  354. .rates = SNDRV_PCM_RATE_8000_192000,
  355. .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
  356. SNDRV_PCM_FMTBIT_S32,
  357. },
  358. .capture = {
  359. .stream_name = "Capture Sense",
  360. .channels_min = 1,
  361. .channels_max = 1,
  362. .rates = SNDRV_PCM_RATE_8000_192000,
  363. .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
  364. SNDRV_PCM_FMTBIT_S32,
  365. },
  366. .ops = &ssm4567_dai_ops,
  367. };
  368. static struct snd_soc_codec_driver ssm4567_codec_driver = {
  369. .set_bias_level = ssm4567_set_bias_level,
  370. .idle_bias_off = true,
  371. .component_driver = {
  372. .controls = ssm4567_snd_controls,
  373. .num_controls = ARRAY_SIZE(ssm4567_snd_controls),
  374. .dapm_widgets = ssm4567_dapm_widgets,
  375. .num_dapm_widgets = ARRAY_SIZE(ssm4567_dapm_widgets),
  376. .dapm_routes = ssm4567_routes,
  377. .num_dapm_routes = ARRAY_SIZE(ssm4567_routes),
  378. },
  379. };
  380. static const struct regmap_config ssm4567_regmap_config = {
  381. .val_bits = 8,
  382. .reg_bits = 8,
  383. .max_register = SSM4567_REG_SOFT_RESET,
  384. .readable_reg = ssm4567_readable_reg,
  385. .writeable_reg = ssm4567_writeable_reg,
  386. .volatile_reg = ssm4567_volatile_reg,
  387. .cache_type = REGCACHE_RBTREE,
  388. .reg_defaults = ssm4567_reg_defaults,
  389. .num_reg_defaults = ARRAY_SIZE(ssm4567_reg_defaults),
  390. };
  391. static int ssm4567_i2c_probe(struct i2c_client *i2c,
  392. const struct i2c_device_id *id)
  393. {
  394. struct ssm4567 *ssm4567;
  395. int ret;
  396. ssm4567 = devm_kzalloc(&i2c->dev, sizeof(*ssm4567), GFP_KERNEL);
  397. if (ssm4567 == NULL)
  398. return -ENOMEM;
  399. i2c_set_clientdata(i2c, ssm4567);
  400. ssm4567->regmap = devm_regmap_init_i2c(i2c, &ssm4567_regmap_config);
  401. if (IS_ERR(ssm4567->regmap))
  402. return PTR_ERR(ssm4567->regmap);
  403. ret = regmap_write(ssm4567->regmap, SSM4567_REG_SOFT_RESET, 0x00);
  404. if (ret)
  405. return ret;
  406. ret = ssm4567_set_power(ssm4567, false);
  407. if (ret)
  408. return ret;
  409. return snd_soc_register_codec(&i2c->dev, &ssm4567_codec_driver,
  410. &ssm4567_dai, 1);
  411. }
  412. static int ssm4567_i2c_remove(struct i2c_client *client)
  413. {
  414. snd_soc_unregister_codec(&client->dev);
  415. return 0;
  416. }
  417. static const struct i2c_device_id ssm4567_i2c_ids[] = {
  418. { "ssm4567", 0 },
  419. { }
  420. };
  421. MODULE_DEVICE_TABLE(i2c, ssm4567_i2c_ids);
  422. #ifdef CONFIG_ACPI
  423. static const struct acpi_device_id ssm4567_acpi_match[] = {
  424. { "INT343B", 0 },
  425. {},
  426. };
  427. MODULE_DEVICE_TABLE(acpi, ssm4567_acpi_match);
  428. #endif
  429. static struct i2c_driver ssm4567_driver = {
  430. .driver = {
  431. .name = "ssm4567",
  432. .acpi_match_table = ACPI_PTR(ssm4567_acpi_match),
  433. },
  434. .probe = ssm4567_i2c_probe,
  435. .remove = ssm4567_i2c_remove,
  436. .id_table = ssm4567_i2c_ids,
  437. };
  438. module_i2c_driver(ssm4567_driver);
  439. MODULE_DESCRIPTION("ASoC SSM4567 driver");
  440. MODULE_AUTHOR("Anatol Pomozov <anatol@chromium.org>");
  441. MODULE_LICENSE("GPL");