| /* |
| * max98090.c -- MAX98090 ALSA SoC Audio driver |
| * |
| * Copyright 2011 Maxim Integrated Products |
| * Copyright 2013 Google Inc. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| */ |
| |
| #include <libpayload.h> |
| |
| #include "drivers/bus/i2c/i2c.h" |
| #include "drivers/sound/max98090.h" |
| |
| static int max98090_i2c_write(Max98090Codec *codec, uint8_t reg, uint8_t data) |
| { |
| return i2c_writeb(codec->i2c, codec->chip, reg, data); |
| } |
| |
| static int max98090_i2c_read(Max98090Codec *codec, uint8_t reg, uint8_t *data) |
| { |
| return i2c_readb(codec->i2c, codec->chip, reg, data); |
| } |
| |
| static int max98090_update_bits(Max98090Codec *codec, uint8_t reg, |
| uint8_t mask, uint8_t value) |
| { |
| uint8_t old; |
| if (max98090_i2c_read(codec, reg, &old)) |
| return 1; |
| uint8_t new_value = (old & ~mask) | (value & mask); |
| if (old != new_value && max98090_i2c_write(codec, reg, new_value)) |
| return 1; |
| |
| return 0; |
| } |
| |
| static int max98090_hw_params(Max98090Codec *codec) |
| { |
| unsigned char value; |
| unsigned int sample_rate = codec->sample_rate; |
| unsigned int bits_per_sample = codec->bits_per_sample; |
| |
| switch (bits_per_sample) { |
| case 16: |
| max98090_i2c_read(codec, M98090_REG_INTERFACE_FORMAT, &value); |
| if (max98090_update_bits(codec, M98090_REG_INTERFACE_FORMAT, |
| M98090_WS_MASK, 0)) |
| return 1; |
| max98090_i2c_read(codec, M98090_REG_INTERFACE_FORMAT, &value); |
| break; |
| default: |
| printf("%s: Illegal bits per sample %d.\n", |
| __func__, bits_per_sample); |
| return 1; |
| } |
| |
| /* Update filter mode */ |
| int mode_mask = (sample_rate < 24000) ? 0 : M98090_MODE_MASK; |
| if (max98090_update_bits(codec, M98090_REG_FILTER_CONFIG, |
| M98090_MODE_MASK, mode_mask)) |
| return 1; |
| |
| /* Update sample rate mode */ |
| int rate = (sample_rate < 50000) ? 0 : M98090_DHF_MASK; |
| if (max98090_update_bits(codec, M98090_REG_FILTER_CONFIG, |
| M98090_DHF_MASK, rate)) |
| return 1; |
| return 0; |
| } |
| |
| // Configures audio interface system clock for the selected frequency. |
| static int max98090_set_sysclk(Max98090Codec *codec) |
| { |
| int freq = codec->lr_frame_size * codec->sample_rate; |
| // TODO(hungte) Should we handle codec->master_clock? |
| uint8_t mclksel = 0; |
| |
| /* |
| * Setup clocks for slave mode, and using the PLL |
| * PSCLK = 0x01 (when master clk is 10MHz to 20MHz) |
| * 0x02 (when master clk is 20MHz to 40MHz).. |
| * 0x03 (when master clk is 40MHz to 60MHz).. |
| */ |
| if ((freq >= 10 * MHz) && (freq < 20 * MHz)) { |
| mclksel |= M98090_PSCLK_DIV1; |
| } else if ((freq >= 20 * MHz) && (freq < 40 * MHz)) { |
| mclksel |= M98090_PSCLK_DIV2; |
| } else if ((freq >= 40 * MHz) && (freq < 60 * MHz)) { |
| mclksel |= M98090_PSCLK_DIV4; |
| } else { |
| printf("%s: Invalid master clock frequency\n", __func__); |
| return 1; |
| } |
| |
| if (max98090_i2c_write(codec, M98090_REG_SYSTEM_CLOCK, mclksel)) |
| return 1; |
| |
| printf("%s: Clock at %uHz\n", __func__, freq); |
| return 0; |
| } |
| |
| // Sets Max98090 I2S format. |
| static int max98090_set_fmt(Max98090Codec *codec) |
| { |
| // Set to slave mode PLL - MAS mode off. |
| if (max98090_i2c_write(codec, M98090_REG_CLOCK_RATIO_NI_MSB, 0x00) || |
| max98090_i2c_write(codec, M98090_REG_CLOCK_RATIO_NI_LSB, 0x00)) |
| return 1; |
| |
| if (max98090_update_bits(codec, M98090_REG_CLOCK_MODE, |
| M98090_USE_M1_MASK, 0)) |
| return 1; |
| |
| if (max98090_i2c_write(codec, M98090_REG_MASTER_MODE, 0)) |
| return 1; |
| |
| // Format: I2S, IB_IF. |
| if (max98090_i2c_write(codec, M98090_REG_INTERFACE_FORMAT, |
| M98090_DLY_MASK | M98090_BCI_MASK | |
| M98090_WCI_MASK)) |
| return 1; |
| |
| return 0; |
| } |
| |
| // Resets the audio codec. |
| static int max98090_reset(Max98090Codec *codec) |
| { |
| // Gracefully reset the DSP core and the codec hardware in a proper |
| // sequence. |
| if (max98090_i2c_write(codec, M98090_REG_SOFTWARE_RESET, |
| M98090_SWRESET_MASK)) |
| return 1; |
| |
| mdelay(20); |
| return 0; |
| } |
| |
| // Initialize max98090 codec device. |
| int max98090_device_init(Max98090Codec *codec) |
| { |
| // Reset the codec, the DSP core, and disable all interrupts. |
| if (max98090_reset(codec)) |
| return 1; |
| |
| |
| uint8_t id; |
| if (max98090_i2c_read(codec, M98090_REG_REVISION_ID, &id)) |
| return 1; |
| printf("%s: Hardware revision: %c\n", __func__, (id - 0x40) + 'A'); |
| |
| /* Reading interrupt status to clear them */ |
| int res = 0; |
| res = max98090_i2c_read(codec, M98090_REG_DEVICE_STATUS, &id); |
| |
| res |= max98090_i2c_write(codec, M98090_REG_DAC_CONTROL, |
| M98090_DACHP_MASK); |
| res |= max98090_i2c_write(codec, M98090_REG_BIAS_CONTROL, |
| M98090_VCM_MODE_MASK); |
| |
| res |= max98090_i2c_write(codec, M98090_REG_LEFT_SPK_MIXER, 0x1); |
| res |= max98090_i2c_write(codec, M98090_REG_RIGHT_SPK_MIXER, 0x2); |
| |
| res |= max98090_i2c_write(codec, M98090_REG_LEFT_SPK_VOLUME, 0x25); |
| res |= max98090_i2c_write(codec, M98090_REG_RIGHT_SPK_VOLUME, 0x25); |
| |
| res |= max98090_i2c_write(codec, M98090_REG_CLOCK_RATIO_NI_MSB, 0x0); |
| res |= max98090_i2c_write(codec, M98090_REG_CLOCK_RATIO_NI_LSB, 0x0); |
| res |= max98090_i2c_write(codec, M98090_REG_MASTER_MODE, 0x0); |
| res |= max98090_i2c_write(codec, M98090_REG_INTERFACE_FORMAT, 0x0); |
| res |= max98090_i2c_write(codec, M98090_REG_IO_CONFIGURATION, |
| M98090_SDIEN_MASK); |
| res |= max98090_i2c_write(codec, M98090_REG_DEVICE_SHUTDOWN, |
| M98090_SHDNN_MASK); |
| res |= max98090_i2c_write(codec, M98090_REG_OUTPUT_ENABLE, |
| M98090_HPREN_MASK | M98090_HPLEN_MASK | |
| M98090_SPREN_MASK | M98090_SPLEN_MASK | |
| M98090_DAREN_MASK | M98090_DALEN_MASK); |
| res |= max98090_i2c_write(codec, M98090_REG_IO_CONFIGURATION, |
| M98090_SDOEN_MASK | M98090_SDIEN_MASK); |
| |
| return res; |
| } |
| |
| static int max98090_enable(SoundRouteComponentOps *me) |
| { |
| Max98090Codec *codec = container_of(me, Max98090Codec, component.ops); |
| |
| return max98090_device_init(codec) || max98090_set_sysclk(codec) || |
| max98090_hw_params(codec) || max98090_set_fmt(codec); |
| } |
| |
| Max98090Codec *new_max98090_codec(I2cOps *i2c, uint8_t chip, |
| int bits_per_sample, int sample_rate, |
| int lr_frame_size, uint8_t master_clock) |
| { |
| Max98090Codec *codec = xzalloc(sizeof(*codec)); |
| |
| codec->component.ops.enable = &max98090_enable; |
| |
| codec->i2c = i2c; |
| codec->chip = chip; |
| |
| codec->bits_per_sample = bits_per_sample; |
| codec->sample_rate = sample_rate; |
| codec->lr_frame_size = lr_frame_size; |
| |
| codec->master_clock = master_clock; |
| return codec; |
| } |