ad193x.c (15529B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * AD193X Audio Codec driver supporting AD1936/7/8/9 4 * 5 * Copyright 2010 Analog Devices Inc. 6 */ 7 8#include <linux/module.h> 9#include <linux/kernel.h> 10#include <linux/device.h> 11#include <linux/regmap.h> 12#include <linux/slab.h> 13#include <sound/core.h> 14#include <sound/pcm.h> 15#include <sound/pcm_params.h> 16#include <sound/initval.h> 17#include <sound/soc.h> 18#include <sound/tlv.h> 19 20#include "ad193x.h" 21 22/* codec private data */ 23struct ad193x_priv { 24 struct regmap *regmap; 25 enum ad193x_type type; 26 int sysclk; 27}; 28 29/* 30 * AD193X volume/mute/de-emphasis etc. controls 31 */ 32static const char * const ad193x_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"}; 33 34static SOC_ENUM_SINGLE_DECL(ad193x_deemp_enum, AD193X_DAC_CTRL2, 1, 35 ad193x_deemp); 36 37static const DECLARE_TLV_DB_MINMAX(adau193x_tlv, -9563, 0); 38 39static const unsigned int ad193x_sb[] = {32}; 40 41static struct snd_pcm_hw_constraint_list constr = { 42 .list = ad193x_sb, 43 .count = ARRAY_SIZE(ad193x_sb), 44}; 45 46static const struct snd_kcontrol_new ad193x_snd_controls[] = { 47 /* DAC volume control */ 48 SOC_DOUBLE_R_TLV("DAC1 Volume", AD193X_DAC_L1_VOL, 49 AD193X_DAC_R1_VOL, 0, 0xFF, 1, adau193x_tlv), 50 SOC_DOUBLE_R_TLV("DAC2 Volume", AD193X_DAC_L2_VOL, 51 AD193X_DAC_R2_VOL, 0, 0xFF, 1, adau193x_tlv), 52 SOC_DOUBLE_R_TLV("DAC3 Volume", AD193X_DAC_L3_VOL, 53 AD193X_DAC_R3_VOL, 0, 0xFF, 1, adau193x_tlv), 54 SOC_DOUBLE_R_TLV("DAC4 Volume", AD193X_DAC_L4_VOL, 55 AD193X_DAC_R4_VOL, 0, 0xFF, 1, adau193x_tlv), 56 57 /* DAC switch control */ 58 SOC_DOUBLE("DAC1 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL1_MUTE, 59 AD193X_DACR1_MUTE, 1, 1), 60 SOC_DOUBLE("DAC2 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL2_MUTE, 61 AD193X_DACR2_MUTE, 1, 1), 62 SOC_DOUBLE("DAC3 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL3_MUTE, 63 AD193X_DACR3_MUTE, 1, 1), 64 SOC_DOUBLE("DAC4 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL4_MUTE, 65 AD193X_DACR4_MUTE, 1, 1), 66 67 /* DAC de-emphasis */ 68 SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum), 69}; 70 71static const struct snd_kcontrol_new ad193x_adc_snd_controls[] = { 72 /* ADC switch control */ 73 SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE, 74 AD193X_ADCR1_MUTE, 1, 1), 75 SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE, 76 AD193X_ADCR2_MUTE, 1, 1), 77 78 /* ADC high-pass filter */ 79 SOC_SINGLE("ADC High Pass Filter Switch", AD193X_ADC_CTRL0, 80 AD193X_ADC_HIGHPASS_FILTER, 1, 0), 81}; 82 83static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = { 84 SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), 85 SND_SOC_DAPM_PGA("DAC Output", AD193X_DAC_CTRL0, 0, 1, NULL, 0), 86 SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0), 87 SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0), 88 SND_SOC_DAPM_VMID("VMID"), 89 SND_SOC_DAPM_OUTPUT("DAC1OUT"), 90 SND_SOC_DAPM_OUTPUT("DAC2OUT"), 91 SND_SOC_DAPM_OUTPUT("DAC3OUT"), 92 SND_SOC_DAPM_OUTPUT("DAC4OUT"), 93}; 94 95static const struct snd_soc_dapm_widget ad193x_adc_widgets[] = { 96 SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), 97 SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0), 98 SND_SOC_DAPM_INPUT("ADC1IN"), 99 SND_SOC_DAPM_INPUT("ADC2IN"), 100}; 101 102static int ad193x_check_pll(struct snd_soc_dapm_widget *source, 103 struct snd_soc_dapm_widget *sink) 104{ 105 struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); 106 struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); 107 108 return !!ad193x->sysclk; 109} 110 111static const struct snd_soc_dapm_route audio_paths[] = { 112 { "DAC", NULL, "SYSCLK" }, 113 { "DAC Output", NULL, "DAC" }, 114 { "DAC Output", NULL, "VMID" }, 115 { "DAC1OUT", NULL, "DAC Output" }, 116 { "DAC2OUT", NULL, "DAC Output" }, 117 { "DAC3OUT", NULL, "DAC Output" }, 118 { "DAC4OUT", NULL, "DAC Output" }, 119 { "SYSCLK", NULL, "PLL_PWR", &ad193x_check_pll }, 120}; 121 122static const struct snd_soc_dapm_route ad193x_adc_audio_paths[] = { 123 { "ADC", NULL, "SYSCLK" }, 124 { "ADC", NULL, "ADC_PWR" }, 125 { "ADC", NULL, "ADC1IN" }, 126 { "ADC", NULL, "ADC2IN" }, 127}; 128 129static inline bool ad193x_has_adc(const struct ad193x_priv *ad193x) 130{ 131 switch (ad193x->type) { 132 case AD1933: 133 case AD1934: 134 return false; 135 default: 136 break; 137 } 138 139 return true; 140} 141 142/* 143 * DAI ops entries 144 */ 145 146static int ad193x_mute(struct snd_soc_dai *dai, int mute, int direction) 147{ 148 struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(dai->component); 149 150 if (mute) 151 regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2, 152 AD193X_DAC_MASTER_MUTE, 153 AD193X_DAC_MASTER_MUTE); 154 else 155 regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2, 156 AD193X_DAC_MASTER_MUTE, 0); 157 158 return 0; 159} 160 161static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, 162 unsigned int rx_mask, int slots, int width) 163{ 164 struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(dai->component); 165 unsigned int channels; 166 167 switch (slots) { 168 case 2: 169 channels = AD193X_2_CHANNELS; 170 break; 171 case 4: 172 channels = AD193X_4_CHANNELS; 173 break; 174 case 8: 175 channels = AD193X_8_CHANNELS; 176 break; 177 case 16: 178 channels = AD193X_16_CHANNELS; 179 break; 180 default: 181 return -EINVAL; 182 } 183 184 regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1, 185 AD193X_DAC_CHAN_MASK, channels << AD193X_DAC_CHAN_SHFT); 186 if (ad193x_has_adc(ad193x)) 187 regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, 188 AD193X_ADC_CHAN_MASK, 189 channels << AD193X_ADC_CHAN_SHFT); 190 191 return 0; 192} 193 194static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, 195 unsigned int fmt) 196{ 197 struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(codec_dai->component); 198 unsigned int adc_serfmt = 0; 199 unsigned int dac_serfmt = 0; 200 unsigned int adc_fmt = 0; 201 unsigned int dac_fmt = 0; 202 203 /* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S 204 * with TDM), ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A) and DAC I2S mode 205 * (SND_SOC_DAIFMT_I2S) 206 */ 207 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 208 case SND_SOC_DAIFMT_I2S: 209 adc_serfmt |= AD193X_ADC_SERFMT_TDM; 210 dac_serfmt |= AD193X_DAC_SERFMT_STEREO; 211 break; 212 case SND_SOC_DAIFMT_DSP_A: 213 adc_serfmt |= AD193X_ADC_SERFMT_AUX; 214 dac_serfmt |= AD193X_DAC_SERFMT_TDM; 215 break; 216 default: 217 if (ad193x_has_adc(ad193x)) 218 return -EINVAL; 219 } 220 221 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 222 case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */ 223 break; 224 case SND_SOC_DAIFMT_NB_IF: /* normal bclk + invert frm */ 225 adc_fmt |= AD193X_ADC_LEFT_HIGH; 226 dac_fmt |= AD193X_DAC_LEFT_HIGH; 227 break; 228 case SND_SOC_DAIFMT_IB_NF: /* invert bclk + normal frm */ 229 adc_fmt |= AD193X_ADC_BCLK_INV; 230 dac_fmt |= AD193X_DAC_BCLK_INV; 231 break; 232 case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */ 233 adc_fmt |= AD193X_ADC_LEFT_HIGH; 234 adc_fmt |= AD193X_ADC_BCLK_INV; 235 dac_fmt |= AD193X_DAC_LEFT_HIGH; 236 dac_fmt |= AD193X_DAC_BCLK_INV; 237 break; 238 default: 239 return -EINVAL; 240 } 241 242 /* For DSP_*, LRCLK's polarity must be inverted */ 243 if (fmt & SND_SOC_DAIFMT_DSP_A) 244 dac_fmt ^= AD193X_DAC_LEFT_HIGH; 245 246 switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 247 case SND_SOC_DAIFMT_CBP_CFP: 248 adc_fmt |= AD193X_ADC_LCR_MASTER; 249 adc_fmt |= AD193X_ADC_BCLK_MASTER; 250 dac_fmt |= AD193X_DAC_LCR_MASTER; 251 dac_fmt |= AD193X_DAC_BCLK_MASTER; 252 break; 253 case SND_SOC_DAIFMT_CBC_CFP: 254 adc_fmt |= AD193X_ADC_LCR_MASTER; 255 dac_fmt |= AD193X_DAC_LCR_MASTER; 256 break; 257 case SND_SOC_DAIFMT_CBP_CFC: 258 adc_fmt |= AD193X_ADC_BCLK_MASTER; 259 dac_fmt |= AD193X_DAC_BCLK_MASTER; 260 break; 261 case SND_SOC_DAIFMT_CBC_CFC: 262 break; 263 default: 264 return -EINVAL; 265 } 266 267 if (ad193x_has_adc(ad193x)) { 268 regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1, 269 AD193X_ADC_SERFMT_MASK, adc_serfmt); 270 regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, 271 AD193X_ADC_FMT_MASK, adc_fmt); 272 } 273 regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL0, 274 AD193X_DAC_SERFMT_MASK, dac_serfmt); 275 regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1, 276 AD193X_DAC_FMT_MASK, dac_fmt); 277 278 return 0; 279} 280 281static int ad193x_set_dai_sysclk(struct snd_soc_dai *codec_dai, 282 int clk_id, unsigned int freq, int dir) 283{ 284 struct snd_soc_component *component = codec_dai->component; 285 struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 286 struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); 287 288 if (clk_id == AD193X_SYSCLK_MCLK) { 289 /* MCLK must be 512 x fs */ 290 if (dir == SND_SOC_CLOCK_OUT || freq != 24576000) 291 return -EINVAL; 292 293 regmap_update_bits(ad193x->regmap, AD193X_PLL_CLK_CTRL1, 294 AD193X_PLL_SRC_MASK, 295 AD193X_PLL_DAC_SRC_MCLK | 296 AD193X_PLL_CLK_SRC_MCLK); 297 298 snd_soc_dapm_sync(dapm); 299 return 0; 300 } 301 switch (freq) { 302 case 12288000: 303 case 18432000: 304 case 24576000: 305 case 36864000: 306 ad193x->sysclk = freq; 307 return 0; 308 } 309 return -EINVAL; 310} 311 312static int ad193x_hw_params(struct snd_pcm_substream *substream, 313 struct snd_pcm_hw_params *params, 314 struct snd_soc_dai *dai) 315{ 316 int word_len = 0, master_rate = 0; 317 struct snd_soc_component *component = dai->component; 318 struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); 319 bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 320 u8 dacc0; 321 322 dev_dbg(dai->dev, "%s() rate=%u format=%#x width=%u channels=%u\n", 323 __func__, params_rate(params), params_format(params), 324 params_width(params), params_channels(params)); 325 326 327 /* bit size */ 328 switch (params_width(params)) { 329 case 16: 330 word_len = 3; 331 break; 332 case 20: 333 word_len = 1; 334 break; 335 case 24: 336 case 32: 337 word_len = 0; 338 break; 339 } 340 341 switch (ad193x->sysclk) { 342 case 12288000: 343 master_rate = AD193X_PLL_INPUT_256; 344 break; 345 case 18432000: 346 master_rate = AD193X_PLL_INPUT_384; 347 break; 348 case 24576000: 349 master_rate = AD193X_PLL_INPUT_512; 350 break; 351 case 36864000: 352 master_rate = AD193X_PLL_INPUT_768; 353 break; 354 } 355 356 if (is_playback) { 357 switch (params_rate(params)) { 358 case 48000: 359 dacc0 = AD193X_DAC_SR_48; 360 break; 361 case 96000: 362 dacc0 = AD193X_DAC_SR_96; 363 break; 364 case 192000: 365 dacc0 = AD193X_DAC_SR_192; 366 break; 367 default: 368 dev_err(dai->dev, "invalid sampling rate: %d\n", params_rate(params)); 369 return -EINVAL; 370 } 371 372 regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL0, AD193X_DAC_SR_MASK, dacc0); 373 } 374 375 regmap_update_bits(ad193x->regmap, AD193X_PLL_CLK_CTRL0, 376 AD193X_PLL_INPUT_MASK, master_rate); 377 378 regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2, 379 AD193X_DAC_WORD_LEN_MASK, 380 word_len << AD193X_DAC_WORD_LEN_SHFT); 381 382 if (ad193x_has_adc(ad193x)) 383 regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1, 384 AD193X_ADC_WORD_LEN_MASK, word_len); 385 386 return 0; 387} 388 389static int ad193x_startup(struct snd_pcm_substream *substream, 390 struct snd_soc_dai *dai) 391{ 392 return snd_pcm_hw_constraint_list(substream->runtime, 0, 393 SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 394 &constr); 395} 396 397static const struct snd_soc_dai_ops ad193x_dai_ops = { 398 .startup = ad193x_startup, 399 .hw_params = ad193x_hw_params, 400 .mute_stream = ad193x_mute, 401 .set_tdm_slot = ad193x_set_tdm_slot, 402 .set_sysclk = ad193x_set_dai_sysclk, 403 .set_fmt = ad193x_set_dai_fmt, 404 .no_capture_mute = 1, 405}; 406 407/* codec DAI instance */ 408static struct snd_soc_dai_driver ad193x_dai = { 409 .name = "ad193x-hifi", 410 .playback = { 411 .stream_name = "Playback", 412 .channels_min = 2, 413 .channels_max = 8, 414 .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000, 415 .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | 416 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, 417 }, 418 .capture = { 419 .stream_name = "Capture", 420 .channels_min = 2, 421 .channels_max = 4, 422 .rates = SNDRV_PCM_RATE_48000, 423 .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | 424 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, 425 }, 426 .ops = &ad193x_dai_ops, 427}; 428 429/* codec DAI instance for DAC only */ 430static struct snd_soc_dai_driver ad193x_no_adc_dai = { 431 .name = "ad193x-hifi", 432 .playback = { 433 .stream_name = "Playback", 434 .channels_min = 2, 435 .channels_max = 8, 436 .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000, 437 .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | 438 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, 439 }, 440 .ops = &ad193x_dai_ops, 441}; 442 443/* codec register values to set after reset */ 444static void ad193x_reg_default_init(struct ad193x_priv *ad193x) 445{ 446 static const struct reg_sequence reg_init[] = { 447 { 0, 0x99 }, /* PLL_CLK_CTRL0: pll input: mclki/xi 12.288Mhz */ 448 { 1, 0x04 }, /* PLL_CLK_CTRL1: no on-chip Vref */ 449 { 2, 0x40 }, /* DAC_CTRL0: TDM mode */ 450 { 3, 0x00 }, /* DAC_CTRL1: reset */ 451 { 4, 0x1A }, /* DAC_CTRL2: 48kHz de-emphasis, unmute dac */ 452 { 5, 0x00 }, /* DAC_CHNL_MUTE: unmute DAC channels */ 453 { 6, 0x00 }, /* DAC_L1_VOL: no attenuation */ 454 { 7, 0x00 }, /* DAC_R1_VOL: no attenuation */ 455 { 8, 0x00 }, /* DAC_L2_VOL: no attenuation */ 456 { 9, 0x00 }, /* DAC_R2_VOL: no attenuation */ 457 { 10, 0x00 }, /* DAC_L3_VOL: no attenuation */ 458 { 11, 0x00 }, /* DAC_R3_VOL: no attenuation */ 459 { 12, 0x00 }, /* DAC_L4_VOL: no attenuation */ 460 { 13, 0x00 }, /* DAC_R4_VOL: no attenuation */ 461 }; 462 static const struct reg_sequence reg_adc_init[] = { 463 { 14, 0x03 }, /* ADC_CTRL0: high-pass filter enable */ 464 { 15, 0x43 }, /* ADC_CTRL1: sata delay=1, adc aux mode */ 465 { 16, 0x00 }, /* ADC_CTRL2: reset */ 466 }; 467 468 regmap_multi_reg_write(ad193x->regmap, reg_init, ARRAY_SIZE(reg_init)); 469 470 if (ad193x_has_adc(ad193x)) { 471 regmap_multi_reg_write(ad193x->regmap, reg_adc_init, 472 ARRAY_SIZE(reg_adc_init)); 473 } 474} 475 476static int ad193x_component_probe(struct snd_soc_component *component) 477{ 478 struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); 479 struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 480 int num, ret; 481 482 /* default setting for ad193x */ 483 ad193x_reg_default_init(ad193x); 484 485 /* adc only */ 486 if (ad193x_has_adc(ad193x)) { 487 /* add adc controls */ 488 num = ARRAY_SIZE(ad193x_adc_snd_controls); 489 ret = snd_soc_add_component_controls(component, 490 ad193x_adc_snd_controls, 491 num); 492 if (ret) 493 return ret; 494 495 /* add adc widgets */ 496 num = ARRAY_SIZE(ad193x_adc_widgets); 497 ret = snd_soc_dapm_new_controls(dapm, 498 ad193x_adc_widgets, 499 num); 500 if (ret) 501 return ret; 502 503 /* add adc routes */ 504 num = ARRAY_SIZE(ad193x_adc_audio_paths); 505 ret = snd_soc_dapm_add_routes(dapm, 506 ad193x_adc_audio_paths, 507 num); 508 if (ret) 509 return ret; 510 } 511 512 return 0; 513} 514 515static const struct snd_soc_component_driver soc_component_dev_ad193x = { 516 .probe = ad193x_component_probe, 517 .controls = ad193x_snd_controls, 518 .num_controls = ARRAY_SIZE(ad193x_snd_controls), 519 .dapm_widgets = ad193x_dapm_widgets, 520 .num_dapm_widgets = ARRAY_SIZE(ad193x_dapm_widgets), 521 .dapm_routes = audio_paths, 522 .num_dapm_routes = ARRAY_SIZE(audio_paths), 523 .idle_bias_on = 1, 524 .use_pmdown_time = 1, 525 .endianness = 1, 526 .non_legacy_dai_naming = 1, 527}; 528 529const struct regmap_config ad193x_regmap_config = { 530 .max_register = AD193X_NUM_REGS - 1, 531}; 532EXPORT_SYMBOL_GPL(ad193x_regmap_config); 533 534int ad193x_probe(struct device *dev, struct regmap *regmap, 535 enum ad193x_type type) 536{ 537 struct ad193x_priv *ad193x; 538 539 if (IS_ERR(regmap)) 540 return PTR_ERR(regmap); 541 542 ad193x = devm_kzalloc(dev, sizeof(*ad193x), GFP_KERNEL); 543 if (ad193x == NULL) 544 return -ENOMEM; 545 546 ad193x->regmap = regmap; 547 ad193x->type = type; 548 549 dev_set_drvdata(dev, ad193x); 550 551 if (ad193x_has_adc(ad193x)) 552 return devm_snd_soc_register_component(dev, &soc_component_dev_ad193x, 553 &ad193x_dai, 1); 554 return devm_snd_soc_register_component(dev, &soc_component_dev_ad193x, 555 &ad193x_no_adc_dai, 1); 556} 557EXPORT_SYMBOL_GPL(ad193x_probe); 558 559MODULE_DESCRIPTION("ASoC ad193x driver"); 560MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); 561MODULE_LICENSE("GPL");