jh7110-evb-can-pdm-pwmdac.dtb \
jh7110-evb-dvp-rgb2hdmi.dtb \
jh7110-evb-pcie-i2s-sd.dtb \
+ jh7110-evb-i2s-ac108.dtb \
jh7110-evb-spi-uart2.dtb \
jh7110-evb-uart1-rgb2hdmi.dtb \
jh7110-evb-uart4-emmc-spdif.dtb \
--- /dev/null
+&sound {
+ /* i2s + ac108 */
+ simple-audio-card,dai-link@0 {
+ reg = <0>;
+ format = "i2s";
+ bitclock-master = <&sndcodec1>;
+ frame-master = <&sndcodec1>;
+
+ widgets =
+ "Microphone", "Mic Jack",
+ "Line", "Line In",
+ "Line", "Line Out",
+ "Speaker", "Speaker",
+ "Headphone", "Headphone Jack";
+ routing =
+ "Headphone Jack", "HP_L",
+ "Headphone Jack", "HP_R",
+ "Speaker", "SPK_LP",
+ "Speaker", "SPK_LN",
+ "LINPUT1", "Mic Jack",
+ "LINPUT3", "Mic Jack",
+ "RINPUT1", "Mic Jack",
+ "RINPUT2", "Mic Jack";
+
+ cpu {
+ sound-dai = <&i2srx_3ch>;
+ };
+ sndcodec1: codec {
+ sound-dai = <&ac108_a>;
+ clocks = <&ac108_mclk>;
+ clock-names = "mclk";
+ };
+ };
+};
#clock-cells = <0>;
clock-frequency = <24576000>;
};
+
+ ac108_mclk: ac108_mclk {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24000000>;
+ };
};
&gmac0 {
status = "okay";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ phy0: ethernet-phy@0 {
+ rxc_dly_en = <0>;
+ tx_delay_sel_fe = <5>;
+ tx_delay_sel = <11>;
+ };
};
&gmac1 {
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
- phy0: ethernet-phy@0 {
+ phy1: ethernet-phy@1 {
rxc-skew-ps = <1060>;
txc-skew-ps = <1800>;
reg = <0>;
&i2srx_3ch {
pinctrl-names = "default";
- pinctrl-0 = <&i2srx_pins>;
+ pinctrl-0 = <&i2s_clk_pins &i2srx_pins>;
status = "disabled";
};
&i2stx_4ch1 {
pinctrl-names = "default";
- pinctrl-0 = <&i2s_clk_pins &i2stx_pins>;
+ pinctrl-0 = <&i2stx_pins>;
status = "disabled";
};
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2022 StarFive Technology Co., Ltd.
+ * Copyright (C) 2022 Hal Feng <hal.feng@starfivetech.com>
+ */
+
+/dts-v1/;
+#include "jh7110-evb.dtsi"
+#include "codecs/sf_ac108.dtsi"
+
+/ {
+ model = "StarFive JH7110 EVB";
+ compatible = "starfive,jh7110-evb", "starfive,jh7110";
+};
+
+
+&i2c0 {
+ status = "okay";
+};
+
+&i2srx_3ch {
+ status = "okay";
+};
&i2stx_4ch1 {
status = "okay";
};
+
+&usbdrd30 {
+ clocks = <&clkgen JH7110_USB_125M>,
+ <&clkgen JH7110_USB0_CLK_APP_125>,
+ <&clkgen JH7110_USB0_CLK_LPM>,
+ <&clkgen JH7110_USB0_CLK_STB>,
+ <&clkgen JH7110_USB0_CLK_USB_APB>,
+ <&clkgen JH7110_USB0_CLK_AXI>,
+ <&clkgen JH7110_USB0_CLK_UTMI_APB>;
+ clock-names = "125m","app","lpm","stb","apb","axi","utmi";
+ resets = <&rstgen RSTN_U0_CDN_USB_PWRUP>,
+ <&rstgen RSTN_U0_CDN_USB_APB>,
+ <&rstgen RSTN_U0_CDN_USB_AXI>,
+ <&rstgen RSTN_U0_CDN_USB_UTMI_APB>;
+ reset-names = "pwrup","apb","axi","utmi";
+ dr_mode = "host"; /*host or peripheral*/
+ starfive,usb2-only;
+ pinctrl-names = "default";
+ pinctrl-0 = <&usb_pins>;
+ status = "okay";
+};
reg = <0x0 0x100a0000 0x0 0x1000>;
clocks = <&clkgen JH7110_SPDIF_CLK_APB>,
<&clkgen JH7110_SPDIF_CLK_CORE>,
- <&clkgen JH7110_APB0>,
<&clkgen JH7110_AUDIO_ROOT>,
<&clkgen JH7110_MCLK_INNER>;
- clock-names = "spdif-apb", "spdif-core", "apb0",
+ clock-names = "spdif-apb", "spdif-core",
"audroot", "mclk_inner";
resets = <&rstgen RSTN_U0_CDNS_SPDIF_APB>;
reset-names = "rst_apb";
<&clkgen JH7110_I2SRX_3CH_BCLK_MST>,
<&clkgen JH7110_I2SRX_3CH_LRCK_MST>,
<&clkgen JH7110_I2SRX0_3CH_BCLK>,
- <&clkgen JH7110_I2SRX0_3CH_LRCK>;
+ <&clkgen JH7110_I2SRX0_3CH_LRCK>,
+ <&clkgen JH7110_MCLK>,
+ <&i2srx_bclk_ext>,
+ <&i2srx_lrck_ext>;
clock-names = "apb0", "3ch-apb",
"audioroot", "mclk-inner",
"bclk_mst", "3ch-lrck",
- "rx-bclk", "rx-lrck";
+ "rx-bclk", "rx-lrck",
+ "mclk", "bclk-ext",
+ "lrck-ext";
resets = <&rstgen RSTN_U0_I2SRX_3CH_APB>,
<&rstgen RSTN_U0_I2SRX_3CH_BCLK>;
dmas = <&dma 24 1>;
CONFIG_SND_STARFIVE_PWMDAC=y
CONFIG_SND_STARFIVE_PDM=y
CONFIG_SND_STARFIVE_TDM=y
+CONFIG_SND_SOC_AC108=y
CONFIG_SND_SOC_SPDIF=y
CONFIG_SND_SOC_WM8960=y
CONFIG_SND_SIMPLE_CARD=y
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/component.h>
-#include<linux/reset.h>
-
+#include <linux/reset.h>
+#include <drm/drm_scdc_helper.h>
#include <drm/bridge/dw_hdmi.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
{148352000, 185440000, 4, 494, 0, 2, 2, 1, 3, 2, 2, 0, 0x816817},
{148500000, 185625000, 4, 495, 0, 2, 2, 1, 3, 2, 2, 0, 0},
{296703000, 296703000, 1, 98, 0, 1, 1, 1, 0, 2, 2, 0, 0xE6AE6B},
- {297000000, 297000000, 1, 99, 0, 1, 1, 1, 0, 2, 2, 0, 0},
+ {297000000, 297000000, 1, 99, 1, 0, 0, 1, 2, 1, 1, 0, 0},
{296703000, 370878750, 4, 494, 1, 2, 0, 1, 3, 1, 1, 0, 0x816817},
{297000000, 371250000, 4, 495, 1, 2, 0, 1, 3, 1, 1, 0, 0},
{593407000, 296703500, 1, 98, 0, 1, 1, 1, 0, 2, 1, 0, 0xE6AE6B},
};
static const struct post_pll_config post_pll_cfg_table[] = {
- {25200000, 1, 80, 7, 0,1},
- {27000000, 1, 40, 3, 0,1},
- {33750000, 1, 40, 8, 3,1},
+ {25200000, 1, 80, 7, 3, 1},
+ {27000000, 1, 40, 11, 3, 1},
+ {33750000, 1, 40, 8, 3, 1},
//{33750000, 1, 80, 8, 2},
- {74250000, 1, 20, 1, 3,1},
+ {74250000, 1, 20, 1, 3, 1},
//{74250000, 18, 80, 8, 2},
- {148500000, 1, 20, 1, 3,3},
- {297000000, 2, 20, 0, 3,3},
- {594000000, 4, 20, 0, 0,0},//postpll_postdiv_en = 0
+ {148500000, 1, 20, 1, 3, 3},
+ {297000000, 4, 20, 0, 0, 3},
+ {594000000, 4, 20, 0, 0, 0},//postpll_postdiv_en = 0
{ /* sentinel */ }
};
-/*
-static const struct post_pll_config post_pll_cfg_table[] = {
- {33750000, 1, 40, 8, 1},
- {33750000, 1, 80, 8, 2},
- {74250000, 1, 40, 8, 1},
- {74250000, 18, 80, 8, 2},
- {148500000, 2, 40, 4, 3},
- {297000000, 4, 40, 2, 3},
- {594000000, 8, 40, 1, 3},
-};
-*/
-static const char coeff_csc[][24] = {
- /*
- * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
- * R = 1.164*Y + 1.596*V - 204
- * G = 1.164*Y - 0.391*U - 0.813*V + 154
- * B = 1.164*Y + 2.018*U - 258
- */
- {
- 0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
- 0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
- 0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
- },
- /*
- * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
- * R = Y + 1.402*V - 248
- * G = Y - 0.344*U - 0.714*V + 135
- * B = Y + 1.772*U - 227
- */
- {
- 0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
- 0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
- 0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
- },
- /*
- * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
- * R = 1.164*Y + 1.793*V - 248
- * G = 1.164*Y - 0.213*U - 0.534*V + 77
- * B = 1.164*Y + 2.115*U - 289
- */
- {
- 0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
- 0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
- 0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
- },
-
- /*
- * RGB2YUV:601 SD mode:
- * Cb = -0.291G - 0.148R + 0.439B + 128
- * Y = 0.504G + 0.257R + 0.098B + 16
- * Cr = -0.368G + 0.439R - 0.071B + 128
- */
- {
- 0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80,
- 0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e,
- 0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80
- },
- /*
- * RGB2YUV:709 HD mode:
- * Cb = - 0.338G - 0.101R + 0.439B + 128
- * Y = 0.614G + 0.183R + 0.062B + 16
- * Cr = - 0.399G + 0.439R - 0.040B + 128
- */
- {
- 0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80,
- 0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10,
- 0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80
- },
- /*
- * RGB[0:255]2RGB[16:235]:
- * R' = R x (235-16)/255 + 16;
- * G' = G x (235-16)/255 + 16;
- * B' = B x (235-16)/255 + 16;
- */
- {
- 0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10,
- 0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
- 0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10
- },
-};
static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
{
static inline void hdmi_writeb(struct inno_hdmi *hdmi, u16 offset, u32 val)
{
+ printk("write: addr 0x%02x, val 0x%02x\n",offset,val);
writel_relaxed(val, hdmi->regs + (offset) * 0x04);
}
}
static void inno_hdmi_tx_phy_power_down(struct inno_hdmi *hdmi)
-{
- //hdmi_write(0x63, word_align(0x00));
- writel_relaxed(0x63, hdmi->regs + (0x00) * 0x04);
+{
+ hdmi_writeb(hdmi, 0x00, 0x63);
}
static void inno_hdmi_config_pll(struct inno_hdmi *hdmi)
{
u8 reg_1ad_value = hdmi->post_cfg->post_div_en ?
- hdmi->post_cfg->postdiv : 0x0d;
+ hdmi->post_cfg->postdiv : 0x00;
+ u8 reg_1aa_value = hdmi->post_cfg->post_div_en ?
+ 0x0e : 0x02;
+ if(1 == hdmi->hdmi_data.vic){
+ reg_1ad_value = 0x0d;
+ }
+
const reg_value_t cfg_pll_data[] = {
{0x1a0, 0x01},
{0x1aa, 0x0f},
{0x1ab, hdmi->post_cfg->prediv},
{0x1ac, hdmi->post_cfg->fbdiv & 0xff},
{0x1ad, reg_1ad_value},
- {0x1aa, 0x0e},
+ {0x1aa, reg_1aa_value},
{0x1a0, 0x00},
};
int i;
for (i = 0; i < sizeof(cfg_pll_data) / sizeof(reg_value_t); i++)
{
- /*
- if(0x1ad == cfg_pll_data[i].reg)
- {
- if(0 == hdmi->post_cfg->post_div_en)
- {
- writel_relaxed(0x0d, hdmi->regs + (cfg_pll_data[i].reg) * 0x04);
- continue;
- }
- }*/
- dev_info(hdmi->dev, "%s %d reg[%02x],val[%02x]\n",__func__, __LINE__,cfg_pll_data[i].reg,cfg_pll_data[i].value);
- writel_relaxed(cfg_pll_data[i].value, hdmi->regs + (cfg_pll_data[i].reg) * 0x04);
+ //dev_info(hdmi->dev, "%s %d reg[%02x],val[%02x]\n",__func__, __LINE__,cfg_pll_data[i].reg,cfg_pll_data[i].value);
+ //writel_relaxed(cfg_pll_data[i].value, hdmi->regs + (cfg_pll_data[i].reg) * 0x04);
+ hdmi_writeb(hdmi, cfg_pll_data[i].reg, cfg_pll_data[i].value);
}
return;
}
static void inno_hdmi_tx_ctrl(struct inno_hdmi *hdmi)
{
- writel_relaxed(0x06, hdmi->regs + (0x9f) * 0x04);
- writel_relaxed(0x82, hdmi->regs + (0xa0) * 0x04);
- writel_relaxed(0xd, hdmi->regs + (0xa2) * 0x04);
- writel_relaxed(0x0, hdmi->regs + (0xa3) * 0x04);
- writel_relaxed(0x0, hdmi->regs + (0xa4) * 0x04);
- writel_relaxed(0x8, hdmi->regs + (0xa5) * 0x04);
- writel_relaxed(0x70, hdmi->regs + (0xa6) * 0x04);
- writel_relaxed(0x10, hdmi->regs + (0xa7) * 0x04);
- writel_relaxed(0x10, hdmi->regs + (0xc9) * 0x04);
+ hdmi_writeb(hdmi, 0x9f, 0x06);
+ hdmi_writeb(hdmi, 0xa7, hdmi->hdmi_data.vic);
}
static void inno_hdmi_tx_phy_param_config(struct inno_hdmi *hdmi)
{
inno_hdmi_config_1920x1080p60(hdmi);
inno_hdmi_tx_ctrl(hdmi);
-
- return;
}
static void inno_hdmi_tx_phy_power_on(struct inno_hdmi *hdmi)
};
int i;
for (i = 0; i < sizeof(pwon_data)/sizeof(reg_value_t); i++) {
- writel_relaxed(pwon_data[i].value, hdmi->regs + (pwon_data[i].reg) * 0x04);
+ //writel_relaxed(pwon_data[i].value, hdmi->regs + (pwon_data[i].reg) * 0x04);
+ hdmi_writeb(hdmi, pwon_data[i].reg, pwon_data[i].value);
}
return;
}
void inno_hdmi_tmds_driver_on(struct inno_hdmi *hdmi)
{
- writel_relaxed(0x8f, hdmi->regs + (0x1b2) * 0x04);
+ //writel_relaxed(0x8f, hdmi->regs + (0x1b2) * 0x04);
+ hdmi_writeb(hdmi, 0x1b2, 0x8f);
}
static void inno_hdmi_init(struct inno_hdmi *hdmi)
{
- writel_relaxed(0x3, hdmi->regs + (0x100) * 0x04);
- writel_relaxed(0xc, hdmi->regs + (0x8) * 0x04);
-
inno_hdmi_power_up(hdmi);
inno_hdmi_tx_phy_power_down(hdmi);
inno_hdmi_tx_phy_param_config(hdmi);
- //inno_hdmi_wait_pll_clk_locked();
inno_hdmi_tx_phy_power_on(hdmi);
inno_hdmi_tmds_driver_on(hdmi);
u32 val;
u32 msk;
+ msk = m_INT_POL;
+ val = v_INT_POL_HIGH;
+ hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
+
hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL);
udelay(100);
hdmi_modb(hdmi, HDMI_SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG);
udelay(100);
- msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL;
- val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | v_INT_POL_HIGH;
- hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
-
inno_hdmi_set_pwr_mode(hdmi, NORMAL);
}
-static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, int setup_rc,
- union hdmi_infoframe *frame, u32 frame_index,
- u32 mask, u32 disable, u32 enable)
-{
-
- if (mask)
- hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable);
-
- hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, frame_index);
-
- if (setup_rc >= 0) {
- u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
- ssize_t rc, i;
-
- rc = hdmi_infoframe_pack(frame, packed_frame,
- sizeof(packed_frame));
- if (rc < 0)
- return rc;
-
- for (i = 0; i < rc; i++)
- hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
- packed_frame[i]);
-
- if (mask)
- hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, enable);
- }
-
- return setup_rc;
-}
-
-static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi,
- struct drm_display_mode *mode)
-{
- union hdmi_infoframe frame;
- int rc;
-
- rc = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
- &hdmi->connector,
- mode);
-
- return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_VSI,
- m_PACKET_VSI_EN, v_PACKET_VSI_EN(0), v_PACKET_VSI_EN(1));
-}
-
-static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
- struct drm_display_mode *mode)
-{
- union hdmi_infoframe frame;
- int rc;
-
- rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
- &hdmi->connector,
- mode);
-
- if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
- frame.avi.colorspace = HDMI_COLORSPACE_YUV444;
- else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
- frame.avi.colorspace = HDMI_COLORSPACE_YUV422;
- else
- frame.avi.colorspace = HDMI_COLORSPACE_RGB;
-
- return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI, 0, 0, 0);
-}
-
-static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
-{
- struct hdmi_data_info *data = &hdmi->hdmi_data;
- int c0_c2_change = 0;
- int csc_enable = 0;
- int csc_mode = 0;
- int auto_csc = 0;
- int value;
- int i;
-
- /* Input video mode is SDR RGB24bit, data enable signal from external */
- hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL1, v_DE_EXTERNAL |
- v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444));
-
- /* Input color hardcode to RGB, and output color hardcode to RGB888 */
- value = v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) |
- v_VIDEO_OUTPUT_COLOR(0) |
- v_VIDEO_INPUT_CSP(0);
- hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
-
- if (data->enc_in_format == data->enc_out_format) {
- if ((data->enc_in_format == HDMI_COLORSPACE_RGB) ||
- (data->enc_in_format >= HDMI_COLORSPACE_YUV444)) {
- value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
- hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
-
- hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
- m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
- v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
- v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
- return 0;
- }
- }
-
- if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
- if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
- (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
- csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
- auto_csc = AUTO_CSC_DISABLE;
- c0_c2_change = C0_C2_CHANGE_DISABLE;
- csc_enable = v_CSC_ENABLE;
- } else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
- (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
- csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
- auto_csc = AUTO_CSC_ENABLE;
- c0_c2_change = C0_C2_CHANGE_DISABLE;
- csc_enable = v_CSC_DISABLE;
- }
- } else {
- if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
- (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
- csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
- auto_csc = AUTO_CSC_DISABLE;
- c0_c2_change = C0_C2_CHANGE_DISABLE;
- csc_enable = v_CSC_ENABLE;
- } else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
- (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
- csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
- auto_csc = AUTO_CSC_ENABLE;
- c0_c2_change = C0_C2_CHANGE_DISABLE;
- csc_enable = v_CSC_DISABLE;
- }
- }
-
- for (i = 0; i < 24; i++)
- hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + i,
- coeff_csc[csc_mode][i]);
-
- value = v_SOF_DISABLE | csc_enable | v_COLOR_DEPTH_NOT_INDICATED(1);
- hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
- hdmi_modb(hdmi, HDMI_VIDEO_CONTRL, m_VIDEO_AUTO_CSC |
- m_VIDEO_C0_C2_SWAP, v_VIDEO_AUTO_CSC(auto_csc) |
- v_VIDEO_C0_C2_SWAP(c0_c2_change));
-
- return 0;
-}
-
-static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
- struct drm_display_mode *mode)
-{
- int value;
-
- /* Set detail external video timing polarity and interlace mode */
- value = v_EXTERANL_VIDEO(1);
- value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
- v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
- value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
- v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
- value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
- v_INETLACE(1) : v_INETLACE(0);
- hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
-
- /* Set detail external video timing */
- value = mode->htotal;
- hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
- hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
-
- value = mode->htotal - mode->hdisplay;
- hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
- hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
-
- value = mode->hsync_start - mode->hdisplay;
- hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
- hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
-
- value = mode->hsync_end - mode->hsync_start;
- hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
- hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
-
- value = mode->vtotal;
- hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
- hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
-
- value = mode->vtotal - mode->vdisplay;
- hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
-
- value = mode->vsync_start - mode->vdisplay;
- hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
-
- value = mode->vsync_end - mode->vsync_start;
- hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
-
- return 0;
-}
-
static const
struct pre_pll_config *inno_hdmi_phy_get_pre_pll_cfg(struct inno_hdmi *hdmi,
unsigned long rate)
- {
- const struct pre_pll_config *cfg = pre_pll_cfg_table;
- //unsigned long tmdsclock = 148500000;
+{
+ const struct pre_pll_config *cfg = pre_pll_cfg_table;
rate = (rate / 1000) * 1000;
- for (; cfg->pixclock != 0; cfg++)
- if (cfg->pixclock == rate)
- break;
+ for (; cfg->pixclock != 0; cfg++)
+ if (cfg->tmdsclock == rate)
+ break;
- if (cfg->pixclock == 0)
- return ERR_PTR(-EINVAL);
+ if (cfg->pixclock == 0)
+ return ERR_PTR(-EINVAL);
- return cfg;
- }
+ return cfg;
+}
#define PRE_PLL_POWER_DOWN BIT(0)
return PTR_ERR(hdmi->pre_cfg);
for (; hdmi->post_cfg->tmdsclock != 0; hdmi->post_cfg++)
- if (tmdsclock <= hdmi->post_cfg->tmdsclock && hdmi->post_cfg->version)
+ if (tmdsclock <= hdmi->post_cfg->tmdsclock)
break;
dev_info(hdmi->dev, "%s hdmi->pre_cfg->pixclock = %lu\n",__func__, hdmi->pre_cfg->pixclock);
dev_info(hdmi->dev, "%s hdmi->post_cfg->version = %d\n",__func__, hdmi->post_cfg->version);
inno_hdmi_config_pll(hdmi);
- //1920x1080p60
//inno_hdmi_tx_ctrl(hdmi);
#if 0 //pre pll + post pll configire
struct drm_display_mode *mode)
{
u8 val;
- int value;
- hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
val = readl_relaxed(hdmi->regs + (0x1b0) * 0x04);
val |= 0x4;
- writel_relaxed(val, hdmi->regs + (0x1b0) * 0x04);
- writel_relaxed(0xf, hdmi->regs + (0x1cc) * 0x04);
-
- /*turn on pre-PLL*/
- val = readl_relaxed(hdmi->regs + (0x1a0) * 0x04);
- val &= ~(0x1);
- writel_relaxed(val, hdmi->regs + (0x1a0) * 0x04);
- /*turn on post-PLL*/
- val = readl_relaxed(hdmi->regs + (0x1aa) * 0x04);
- val &= ~(0x1);
- writel_relaxed(val, hdmi->regs + (0x1aa) * 0x04);
+ //writel_relaxed(val, hdmi->regs + (0x1b0) * 0x04);
+ //writel_relaxed(0xf, hdmi->regs + (0x1cc) * 0x04);
+ hdmi_writeb(hdmi, 0x1b0, val);
+ hdmi_writeb(hdmi, 0x1cc, 0xf);
+ hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
hdmi->tmds_rate = mode->clock * 1000;
inno_hdmi_phy_clk_set_rate(hdmi,hdmi->tmds_rate);
while (!(readl_relaxed(hdmi->regs + (0x1af) * 0x04) & 0x1))
;
- /*turn on LDO*/
- writel_relaxed(0x7, hdmi->regs + (0x1b4) * 0x04);
- /*turn on serializer*/
- writel_relaxed(0x70, hdmi->regs + (0x1be) * 0x04);
+ /*turn on LDO*/
+ hdmi_writeb(hdmi, 0x1b4, 0x7);
+ /*turn on serializer*/
+ hdmi_writeb(hdmi, 0x1be, 0x70);
inno_hdmi_tx_phy_power_down(hdmi);
- /* Set HDMI Mode */
- hdmi_writeb(hdmi, 0x100,0x3);
- //hdmi_writeb(hdmi, 0x8,0x00);
-
- /* Set detail external video timing polarity and interlace mode */
- value = v_EXTERANL_VIDEO(0);
- value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
- v_HSYNC_POLARITY(1) : v_HSYNC_POLARITY(0);
- value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
- v_VSYNC_POLARITY(1) : v_VSYNC_POLARITY(0);
- value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
- v_INETLACE(1) : v_INETLACE(0);
- hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
-
- hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
- hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
- hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
+ hdmi_writeb(hdmi, 0xC9, 0x40);
+ inno_hdmi_tx_ctrl(hdmi);
- if ((hdmi->hdmi_data.vic == 6) || (hdmi->hdmi_data.vic == 7) ||
- (hdmi->hdmi_data.vic == 21) || (hdmi->hdmi_data.vic == 22) ||
- (hdmi->hdmi_data.vic == 2) || (hdmi->hdmi_data.vic == 3) ||
- (hdmi->hdmi_data.vic == 17) || (hdmi->hdmi_data.vic == 18))
- hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
- else
- hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
+ //hdmi_writeb(hdmi, 0x100, 0x02);
- /* Mute audio output */
- hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE ,
- v_AUDIO_MUTE(1));
+ //drm_scdc_set_high_tmds_clock_ratio(hdmi->ddc, true);
+ //drm_scdc_set_scrambling(hdmi->ddc, true);
-
- if(0 == hdmi->hdmi_data.vic)
- {
- inno_hdmi_config_video_timing(hdmi, mode);
-
- inno_hdmi_config_video_csc(hdmi);
- }
-
- if (hdmi->hdmi_data.sink_is_hdmi) {
- inno_hdmi_config_video_avi(hdmi, mode);
- inno_hdmi_config_video_vsi(hdmi, mode);
- }
+ hdmi_writeb(hdmi, 0x35, 0x01);
+ hdmi_writeb(hdmi, 0x38, 0x04);
+ hdmi_writeb(hdmi, 0x40, 0x18);
+ hdmi_writeb(hdmi, 0x41, 0x80);
inno_hdmi_tx_phy_power_on(hdmi);
inno_hdmi_tmds_driver_on(hdmi);
- writel_relaxed(0x0, hdmi->regs + (0xce) * 0x04);
- writel_relaxed(0x1, hdmi->regs + (0xce) * 0x04);
+ hdmi_writeb(hdmi, 0xce, 0x0);
+ hdmi_writeb(hdmi, 0xce, 0x1);
return 0;
}
static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
{
struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
- //inno_hdmi_init(hdmi);
inno_hdmi_set_pwr_mode(hdmi, NORMAL);
}
return (valid) ? MODE_OK : MODE_BAD;
#endif
u32 vic = drm_match_cea_mode(mode);
+ struct drm_display_info *display = &connector->display_info;
+ printk("vic ======== %d-------display->hdmi.scdc.supported = %d",vic,display->hdmi.scdc.supported);
+
+ printk("display->hdmi.scdc.scrambling.supported = %d",display->hdmi.scdc.scrambling.supported);
if (vic >= 1)
return MODE_OK;
inno_hdmi_probe_single_connector_modes(struct drm_connector *connector,
uint32_t maxX, uint32_t maxY)
{
- return drm_helper_probe_single_connector_modes(connector, 1920, 1080);
+ return drm_helper_probe_single_connector_modes(connector, 3840, 2160);//3840x2160
}
static void inno_hdmi_connector_destroy(struct drm_connector *connector)
return adap;
}
-
-
-
-#if 0
-static void inno_hdmi_get_edid(struct inno_hdmi *hdmi, unsigned int tmds_clk, unsigned char *data)
-{
- unsigned int i, ddc_div_msb, ddc_div_lsb;
- unsigned int ddc_scl = 100000; //scl:100k
-
- //hdmi_write(0xff, 0x0c2);
- //hdmi_write(0xff, 0x0c4);
-
- ddc_div_lsb = (tmds_clk/(4*ddc_scl))>>2;
- ddc_div_msb = (tmds_clk/(4*ddc_scl)) & 0x00ff;
- hdmi_writeb(hdmi, 0x04b,ddc_div_lsb);
- hdmi_writeb(hdmi, 0x04c,ddc_div_msb);
- /*enable EDID ready interrupt*/
- hdmi_writeb(hdmi,0x0c0,0x04 );
-
- /*read e-edid segment 0x00 256 bytes steps*/
- /*set EDID FIFO initial address*/
- hdmi_writeb(hdmi,0x04f, 0x00);
- /*set EDID first word address, read first 128 byte*/
- hdmi_writeb(hdmi,0x04e, 0x00);
- /*set EDID segment 0x00 address*/
- hdmi_writeb(hdmi, 0x04d,0x00);
-
- while(!(hdmi_readb(hdmi,0x0c1) & 0x4));
- hdmi_writeb(hdmi,0x0c1, 0x04); // clear ready interrupt: write 1 to bit2
-
- /*read first 128 bytes*/
- for(i = 0; i < 128; i++)
- {
- data[i] = hdmi_readb(hdmi,0x050);
- }
-
- /*set EDID FIFO initial address again*/
- hdmi_writeb(hdmi,0x04f, 0x00);
- /*set EDID first word address, read last 128 byte*/
- hdmi_writeb(hdmi,0x04e, 0x80);
- /*set EDID segment 0x00 address*/
- hdmi_writeb(hdmi,0x04d, 0x00);
-
- while(!(hdmi_readb(hdmi,0x0c1) & 0x4));
- hdmi_writeb(hdmi,0x0c1, 0x04); // clear ready interrupt: write 1 to bit2
-
- /*read last 128 bytes*/
- for(i = 128; i < 256; i++)
- {
- data[i] = hdmi_readb(hdmi,0x050);
- }
-
-}
-#endif
-
static int inno_hdmi_get_clk_rst(struct device *dev, struct inno_hdmi *hdmi)
{
hdmi->sys_clk = devm_clk_get(dev, "sysclk");
goto err_disable_clk;
}
#ifdef CONFIG_DRM_I2C_NXP_TDA998X
+ hdmi->hdmi_data.vic = 0x10;
inno_hdmi_init(hdmi);
#endif
inno_hdmi_reset(hdmi);
encoder->possible_crtcs =
drm_of_find_possible_crtcs(drm_dev, dev->of_node);
- encoder->possible_crtcs = 3;
+ encoder->possible_crtcs = 2;
/* output port is port1*/
/*
* Driver for Motorcomm PHYs
*
- * Author: Peter Geis <pgwipeout@gmail.com>
+ * Author: yinghong.zhang<yinghong.zhang@motor-comm.com>
+ *
+ * Copyright (c) 2019 Motorcomm, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Support : Motorcomm Phys:
+ * Giga phys: yt8511, yt8521, yt8531, yt8614, yt8618
+ * 100/10 Phys : yt8512, yt8512b, yt8510
+ * Automotive 100Mb Phys : yt8010
+ * Automotive 100/10 hyper range Phys: yt8510
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/phy.h>
+#include <linux/of.h>
+#include <linux/clk.h>
-#define PHY_ID_YT8511 0x0000010a
-#define PHY_ID_YT8521 0x0000011a
-#define MOTORCOMM_PHY_ID_MASK 0x00000fff
-
-#define YT8511_PAGE_SELECT 0x1e
-#define YT8511_PAGE 0x1f
-#define YT8511_EXT_CLK_GATE 0x0c
-#define YT8511_EXT_DELAY_DRIVE 0x0d
-#define YT8511_EXT_SLEEP_CTRL 0x27
+/* for wol feature, 20210604 */
+#include <linux/netdevice.h>
-/* 2b00 25m from pll
- * 2b01 25m from xtl *default*
- * 2b10 62.m from pll
- * 2b11 125m from pll
- */
-#define YT8511_CLK_125M (BIT(2) | BIT(1))
-#define YT8511_PLLON_SLP BIT(14)
+#define YT_LINUX_MAJOR 2
+#define YT_LINUX_MINOR 2
+#define YT_LINUX_SUBVERSION 6255
+#define YT_LINUX_VERSIONID "2.2.6255"
-/* RX Delay enabled = 1.8ns 1000T, 8ns 10/100T */
-#define YT8511_DELAY_RX BIT(0)
+/********************************************
+ **** configuration section begin ***********/
-/* TX Gig-E Delay is bits 7:4, default 0x5
- * TX Fast-E Delay is bits 15:12, default 0xf
- * Delay = 150ps * N - 250ps
- * On = 2000ps, off = 50ps
+/* if system depends on ethernet packet to restore from sleep,
+ * please define this macro to 1 otherwise, define it to 0.
*/
-#define YT8511_DELAY_GE_TX_EN (0xf << 4)
-#define YT8511_DELAY_GE_TX_DIS (0x2 << 4)
-#define YT8511_DELAY_FE_TX_EN (0xf << 12)
-#define YT8511_DELAY_FE_TX_DIS (0x2 << 12)
-
-#define YT8521_SLEEP_SW_EN BIT(15)
-#define YT8521_LINK_STATUS BIT(10)
-#define YT8521_DUPLEX 0x2000
-#define YT8521_SPEED_MODE 0xc000
-#define YTPHY_REG_SPACE_UTP 0
-#define YTPHY_REG_SPACE_FIBER 2
-#define REG_PHY_SPEC_STATUS 0x11
-/* based on yt8521 wol config register */
-#define YTPHY_UTP_INTR_REG 0x12
-
-#define SYS_WAKEUP_BASED_ON_ETH_PKT 0
-
-/* to enable system WOL of phy, please define this macro to 1
+#define SYS_WAKEUP_BASED_ON_ETH_PKT 1
+
+/* to enable system WOL feature of phy, please define this macro to 1
* otherwise, define it to 0.
*/
-#define YTPHY_ENABLE_WOL 0
+#define YTPHY_WOL_FEATURE_ENABLE 0
-#if (YTPHY_ENABLE_WOL)
- #undef SYS_WAKEUP_BASED_ON_ETH_PKT
- #define SYS_WAKEUP_BASED_ON_ETH_PKT 1
-#endif
+/* some GMAC need clock input from PHY, for eg., 125M,
+ * please enable this macro
+ * by degault, it is set to 0
+ * NOTE: this macro will need macro SYS_WAKEUP_BASED_ON_ETH_PKT to set to 1
+ */
+#define GMAC_CLOCK_INPUT_NEEDED 0
-#if (YTPHY_ENABLE_WOL)
-enum ytphy_wol_type_e {
- YTPHY_WOL_TYPE_LEVEL,
- YTPHY_WOL_TYPE_PULSE,
- YTPHY_WOL_TYPE_MAX
+/* the max number of yt8521 chip on pcb board
+ * the most case is only 1 chip per board, but
+ * by default, we support up to 8.
+ */
+#define YTPHY_BOARD_MAX_NUM_OF_CHIP_8521 8
+#define YTPHY_BOARD_MAX_NUM_OF_CHIP_8614 4
+
+/* for YT8531 package A xtal init config */
+#define YTPHY8531A_XTAL_INIT (0)
+
+/**** configuration section end ***********
+ ******************************************/
+
+/* no need to change below */
+#define MOTORCOMM_PHY_ID_MASK 0x00000fff
+#define MOTORCOMM_PHY_ID_8531_MASK 0xffffffff
+#define MOTORCOMM_MPHY_ID_MASK 0x0000ffff
+#define MOTORCOMM_MPHY_ID_MASK_8614 0xffffffff
+#define MOTORCOMM_PHY_ID_MASK_8821 0xffffffff
+
+#define PHY_ID_YT8010 0x00000309
+#define PHY_ID_YT8010AS 0x4f51eb19
+#define PHY_ID_YT8510 0x00000109
+#define PHY_ID_YT8511 0x0000010a
+#define PHY_ID_YT8512 0x00000118
+#define PHY_ID_YT8512B 0x00000128
+#define PHY_ID_YT8521 0x0000011a
+#define PHY_ID_YT8531S 0x4f51e91a
+#define PHY_ID_YT8531 0x4f51e91b
+#define PHY_ID_YT8614 0x4F51E899
+#define PHY_ID_YT8618 0x0000e889
+#define PHY_ID_YT8821 0x4f51ea10
+
+#define REG_PHY_SPEC_STATUS 0x11
+#define REG_DEBUG_ADDR_OFFSET 0x1e
+#define REG_DEBUG_DATA 0x1f
+
+#define YT8512_EXTREG_LED0 0x40c0
+#define YT8512_EXTREG_LED1 0x40c3
+
+#define YT8512_EXTREG_SLEEP_CONTROL1 0x2027
+
+#define YT_SOFTWARE_RESET 0x8000
+
+#define YT8512_LED0_ACT_BLK_IND 0x1000
+#define YT8512_LED0_DIS_LED_AN_TRY 0x0001
+#define YT8512_LED0_BT_BLK_EN 0x0002
+#define YT8512_LED0_HT_BLK_EN 0x0004
+#define YT8512_LED0_COL_BLK_EN 0x0008
+#define YT8512_LED0_BT_ON_EN 0x0010
+#define YT8512_LED1_BT_ON_EN 0x0010
+#define YT8512_LED1_TXACT_BLK_EN 0x0100
+#define YT8512_LED1_RXACT_BLK_EN 0x0200
+#define YT8512_SPEED_MODE 0xc000
+#define YT8512_DUPLEX 0x2000
+
+#define YT8512_SPEED_MODE_BIT 14
+#define YT8512_DUPLEX_BIT 13
+#define YT8512_EN_SLEEP_SW_BIT 15
+
+#define YT8521_EXTREG_SLEEP_CONTROL1 0x27
+#define YT8521_EN_SLEEP_SW_BIT 15
+
+#define YT8521_SPEED_MODE 0xc000
+#define YT8521_DUPLEX 0x2000
+#define YT8521_SPEED_MODE_BIT 14
+#define YT8521_DUPLEX_BIT 13
+#define YT8521_LINK_STATUS_BIT 10
+
+/* based on yt8521 wol feature config register */
+#define YTPHY_UTP_INTR_REG 0x12
+/* WOL Feature Event Interrupt Enable */
+#define YTPHY_WOL_FEATURE_INTR BIT(6)
+
+/* Magic Packet MAC address registers */
+#define YTPHY_WOL_FEATURE_MACADDR2_4_MAGIC_PACKET 0xa007
+#define YTPHY_WOL_FEATURE_MACADDR1_4_MAGIC_PACKET 0xa008
+#define YTPHY_WOL_FEATURE_MACADDR0_4_MAGIC_PACKET 0xa009
+
+#define YTPHY_WOL_FEATURE_REG_CFG 0xa00a
+#define YTPHY_WOL_FEATURE_TYPE_CFG BIT(0) /* WOL TYPE Config */
+#define YTPHY_WOL_FEATURE_ENABLE_CFG BIT(3) /* WOL Enable Config */
+#define YTPHY_WOL_FEATURE_INTR_SEL_CFG BIT(6) /* WOL Event Interrupt Enable Config */
+#define YTPHY_WOL_FEATURE_WIDTH1_CFG BIT(1) /* WOL Pulse Width Config */
+#define YTPHY_WOL_FEATURE_WIDTH2_CFG BIT(2) /* WOL Pulse Width Config */
+
+#define YTPHY_REG_SPACE_UTP 0
+#define YTPHY_REG_SPACE_FIBER 2
+
+#define YTPHY_EXTREG_CHIP_CONFIG 0xa001
+#define YTPHY_EXTREG_RGMII_CONFIG1 0xa003
+
+enum ytphy_wol_feature_trigger_type_e {
+ YTPHY_WOL_FEATURE_PULSE_TRIGGER,
+ YTPHY_WOL_FEATURE_LEVEL_TRIGGER,
+ YTPHY_WOL_FEATURE_TRIGGER_TYPE_MAX
};
-typedef enum ytphy_wol_type_e ytphy_wol_type_t;
-
-enum ytphy_wol_width_e {
- YTPHY_WOL_WIDTH_84MS,
- YTPHY_WOL_WIDTH_168MS,
- YTPHY_WOL_WIDTH_336MS,
- YTPHY_WOL_WIDTH_672MS,
- YTPHY_WOL_WIDTH_MAX
+
+enum ytphy_wol_feature_pulse_width_e {
+ YTPHY_WOL_FEATURE_672MS_PULSE_WIDTH,
+ YTPHY_WOL_FEATURE_336MS_PULSE_WIDTH,
+ YTPHY_WOL_FEATURE_168MS_PULSE_WIDTH,
+ YTPHY_WOL_FEATURE_84MS_PULSE_WIDTH,
+ YTPHY_WOL_FEATURE_PULSE_WIDTH_MAX
};
-typedef enum ytphy_wol_width_e ytphy_wol_width_t;
-struct ytphy_wol_cfg_s {
- int enable;
+struct ytphy_wol_feature_cfg {
+ bool enable;
int type;
int width;
};
-typedef struct ytphy_wol_cfg_s ytphy_wol_cfg_t;
-#endif /*(YTPHY_ENABLE_WOL)*/
-static int yt8511_read_page(struct phy_device *phydev)
-{
- return __phy_read(phydev, YT8511_PAGE_SELECT);
-};
+#if (YTPHY_WOL_FEATURE_ENABLE)
+#undef SYS_WAKEUP_BASED_ON_ETH_PKT
+#define SYS_WAKEUP_BASED_ON_ETH_PKT 1
+#endif
-static int yt8511_write_page(struct phy_device *phydev, int page)
-{
- return __phy_write(phydev, YT8511_PAGE_SELECT, page);
+/* YT8521 polling mode */
+#define YT8521_PHY_MODE_FIBER 1 //fiber mode only
+#define YT8521_PHY_MODE_UTP 2 //utp mode only
+#define YT8521_PHY_MODE_POLL 3 //fiber and utp, poll mode
+
+/* below are for bitmap */
+#define YT_PHY_MODE_FIBER 1 //fiber/sgmii mode only
+#define YT_PHY_MODE_UTP 2 //utp mode only
+#define YT_PHY_MODE_QSGMII 4 //qsgmii mode only
+
+//qsgmii, fiber/sgmii and utp, poll mode
+#define YT_PHY_MODE_POLL (YT_PHY_MODE_FIBER |\
+ YT_PHY_MODE_UTP |\
+ YT_PHY_MODE_QSGMII)
+/* support automatically check polling mode for yt8521
+ * for Fiber only system, please define YT8521_PHY_MODE_CURR 1
+ * for UTP only system, please define YT8521_PHY_MODE_CURR 2
+ * for combo system, please define YT8521_PHY_MODE_CURR 3
+ */
+#define YTPHY_861X_ABC_VER 0
+#if (YTPHY_861X_ABC_VER)
+static int yt8614_get_port_from_phydev(struct phy_device *phydev);
+#endif
+static int yt8521_hw_strap_polling(struct phy_device *phydev);
+static int yt8614_hw_strap_polling(struct phy_device *phydev);
+#define YT8521_PHY_MODE_CURR yt8521_hw_strap_polling(phydev)
+#define YT8614_PHY_MODE_CURR yt8614_hw_strap_polling(phydev)
+
+struct ytphy_reg_field {
+ const char *name;
+ const u8 size; /* Size of the bitfield, in bits */
+ const u8 off; /* Offset from bit 0 */
+ const u8 dflt; /* Default value */
};
-static int yt8511_config_init(struct phy_device *phydev)
-{
- int oldpage, ret = 0;
- unsigned int ge, fe;
-
- oldpage = phy_select_page(phydev, YT8511_EXT_CLK_GATE);
- if (oldpage < 0)
- goto err_restore_page;
-
- /* set rgmii delay mode */
- switch (phydev->interface) {
- case PHY_INTERFACE_MODE_RGMII:
- ge = YT8511_DELAY_GE_TX_DIS;
- fe = YT8511_DELAY_FE_TX_DIS;
- break;
- case PHY_INTERFACE_MODE_RGMII_RXID:
- ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_DIS;
- fe = YT8511_DELAY_FE_TX_DIS;
- break;
- case PHY_INTERFACE_MODE_RGMII_TXID:
- ge = YT8511_DELAY_GE_TX_EN;
- fe = YT8511_DELAY_FE_TX_EN;
- break;
- case PHY_INTERFACE_MODE_RGMII_ID:
- ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN;
- fe = YT8511_DELAY_FE_TX_EN;
- break;
- default: /* do not support other modes */
- ret = -EOPNOTSUPP;
- goto err_restore_page;
- }
-
- ret = __phy_modify(phydev, YT8511_PAGE, (YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN), ge);
- if (ret < 0)
- goto err_restore_page;
-
- /* set clock mode to 125mhz */
- ret = __phy_modify(phydev, YT8511_PAGE, 0, YT8511_CLK_125M);
- if (ret < 0)
- goto err_restore_page;
-
- /* fast ethernet delay is in a separate page */
- ret = __phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_DELAY_DRIVE);
- if (ret < 0)
- goto err_restore_page;
+static const struct ytphy_reg_field ytphy_rxtxd_grp[] = {
+ { "rx_delay_sel", 4, 10, 0x0 },
+ { "tx_delay_sel_fe", 4, 4, 0xf },
+ { "tx_delay_sel", 4, 0, 0x1 }
+};
- ret = __phy_modify(phydev, YT8511_PAGE, YT8511_DELAY_FE_TX_EN, fe);
- if (ret < 0)
- goto err_restore_page;
+static const struct ytphy_reg_field ytphy_rxden_grp[] = {
+ { "rxc_dly_en", 1, 8, 0x1 }
+};
- /* leave pll enabled in sleep */
- ret = __phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_SLEEP_CTRL);
- if (ret < 0)
- goto err_restore_page;
+static uint bitfield_mask(uint shift, uint width)
+{
+ return ((1 << width) - 1) << shift;
+}
- ret = __phy_modify(phydev, YT8511_PAGE, 0, YT8511_PLLON_SLP);
- if (ret < 0)
- goto err_restore_page;
+static uint bitfield_replace(uint reg_val, uint shift, uint width,
+ uint bitfield_val)
+{
+ uint mask = bitfield_mask(shift, width);
-err_restore_page:
- return phy_restore_page(phydev, oldpage, ret);
+ return (reg_val & ~mask) | ((bitfield_val << shift) & mask);
}
-int genphy_config_init(struct phy_device *phydev)
+static int ytphy_config_init(struct phy_device *phydev)
{
- return genphy_read_abilities(phydev);
+ int val;
+
+ val = phy_read(phydev, 3);
+
+ return 0;
}
static int ytphy_read_ext(struct phy_device *phydev, u32 regnum)
int ret;
int val;
- ret = phy_write(phydev, YT8511_PAGE_SELECT, regnum);
+ ret = phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum);
if (ret < 0)
return ret;
- val = phy_read(phydev, YT8511_PAGE);
+ val = phy_read(phydev, REG_DEBUG_DATA);
return val;
}
{
int ret;
- ret = phy_write(phydev, YT8511_PAGE_SELECT, regnum);
+ ret = phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum);
if (ret < 0)
return ret;
- ret = phy_write(phydev, YT8511_PAGE, val);
+ ret = phy_write(phydev, REG_DEBUG_DATA, val);
return ret;
}
-int yt8521_soft_reset(struct phy_device *phydev)
+static int ytphy_soft_reset(struct phy_device *phydev)
{
- int ret, val;
+ int ret = 0, val = 0;
- val = ytphy_read_ext(phydev, 0xa001);
- ytphy_write_ext(phydev, 0xa001, (val & ~0x8000));
+ val = phy_read(phydev, MII_BMCR);
+ if (val < 0)
+ return val;
- ret = genphy_soft_reset(phydev);
+ ret = phy_write(phydev, MII_BMCR, val | BMCR_RESET);
if (ret < 0)
return ret;
- return 0;
+ return ret;
}
-#if (YTPHY_ENABLE_WOL)
-static int ytphy_switch_reg_space(struct phy_device *phydev, int space)
+#if (YTPHY8531A_XTAL_INIT)
+static int yt8531a_xtal_init(struct phy_device *phydev)
{
- int ret;
+ int ret = 0;
+ int val = 0;
+ bool state = false;
- if (space == YTPHY_REG_SPACE_UTP)
- ret = ytphy_write_ext(phydev, 0xa000, 0);
- else
- ret = ytphy_write_ext(phydev, 0xa000, 2);
+ msleep(50);
+
+ do {
+ ret = ytphy_write_ext(phydev, 0xa012, 0x88);
+ if (ret < 0)
+ return ret;
+
+ msleep(100);
+
+ val = ytphy_read_ext(phydev, 0xa012);
+ if (val < 0)
+ return val;
+
+ usleep_range(10000, 20000);
+ } while (val != 0x88);
+
+ ret = ytphy_write_ext(phydev, 0xa012, 0xc8);
+ if (ret < 0)
+ return ret;
return ret;
}
+#endif
-static int ytphy_wol_en_cfg(struct phy_device *phydev, ytphy_wol_cfg_t wol_cfg)
+int yt8010_soft_reset(struct phy_device *phydev)
{
- int ret=0;
- int val=0;
+ ytphy_soft_reset(phydev);
- val = ytphy_read_ext(phydev, YTPHY_WOL_CFG_REG);
- if (val < 0)
- return val;
+ return 0;
+}
- if(wol_cfg.enable) {
- val |= YTPHY_WOL_CFG_EN;
-
- if(wol_cfg.type == YTPHY_WOL_TYPE_LEVEL) {
- val &= ~YTPHY_WOL_CFG_TYPE;
- val &= ~YTPHY_WOL_CFG_INTR_SEL;
- } else if(wol_cfg.type == YTPHY_WOL_TYPE_PULSE) {
- val |= YTPHY_WOL_CFG_TYPE;
- val |= YTPHY_WOL_CFG_INTR_SEL;
-
- if(wol_cfg.width == YTPHY_WOL_WIDTH_84MS) {
- val &= ~YTPHY_WOL_CFG_WIDTH1;
- val &= ~YTPHY_WOL_CFG_WIDTH2;
- } else if(wol_cfg.width == YTPHY_WOL_WIDTH_168MS) {
- val |= YTPHY_WOL_CFG_WIDTH1;
- val &= ~YTPHY_WOL_CFG_WIDTH2;
- } else if(wol_cfg.width == YTPHY_WOL_WIDTH_336MS) {
- val &= ~YTPHY_WOL_CFG_WIDTH1;
- val |= YTPHY_WOL_CFG_WIDTH2;
- } else if(wol_cfg.width == YTPHY_WOL_WIDTH_672MS) {
- val |= YTPHY_WOL_CFG_WIDTH1;
- val |= YTPHY_WOL_CFG_WIDTH2;
- }
- }
- } else {
- val &= ~YTPHY_WOL_CFG_EN;
- val &= ~YTPHY_WOL_CFG_INTR_SEL;
+int yt8010AS_soft_reset(struct phy_device *phydev)
+{
+ int ret = 0;
+
+ /* sgmii */
+ ytphy_write_ext(phydev, 0xe, 1);
+ ret = ytphy_soft_reset(phydev);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xe, 0);
+ return ret;
}
- ret = ytphy_write_ext(phydev, YTPHY_WOL_CFG_REG, val);
+ /* utp */
+ ytphy_write_ext(phydev, 0xe, 0);
+ ret = ytphy_soft_reset(phydev);
if (ret < 0)
return ret;
return 0;
}
-static void ytphy_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
+int yt8010_aneg_done(struct phy_device *phydev)
{
int val = 0;
- wol->supported = WAKE_MAGIC;
- wol->wolopts = 0;
-
- val = ytphy_read_ext(phydev, YTPHY_WOL_CFG_REG);
- if (val < 0)
- return;
-
- if (val & YTPHY_WOL_CFG_EN)
- wol->wolopts |= WAKE_MAGIC;
+ val = phy_read(phydev, 0x1);
+ val = phy_read(phydev, 0x1);
- return;
+ return (val < 0) ? val : (val & BMSR_LSTATUS);
}
-static int ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
+static int yt8010_config_aneg(struct phy_device *phydev)
{
- int ret, pre_page, val;
- ytphy_wol_cfg_t wol_cfg;
- struct net_device *p_attached_dev = phydev->attached_dev;
+ phydev->speed = SPEED_100;
+ return 0;
+}
- memset(&wol_cfg,0,sizeof(ytphy_wol_cfg_t));
- pre_page = ytphy_read_ext(phydev, 0xa000);
- if (pre_page < 0)
- return pre_page;
+static int yt8010_read_status(struct phy_device *phydev)
+{
+ int ret = 0;
- /* Switch to phy UTP page */
- ret = ytphy_switch_reg_space(phydev, YTPHY_REG_SPACE_UTP);
- if (ret < 0)
+ ret = genphy_update_link(phydev);
+ if (ret)
return ret;
- if (wol->wolopts & WAKE_MAGIC) {
- /* Enable the WOL interrupt */
- val = phy_read(phydev, YTPHY_UTP_INTR_REG);
- val |= YTPHY_WOL_INTR;
- ret = phy_write(phydev, YTPHY_UTP_INTR_REG, val);
- if (ret < 0)
- return ret;
-
- /* Set the WOL config */
- wol_cfg.enable = 1; //enable
- wol_cfg.type= YTPHY_WOL_TYPE_PULSE;
- wol_cfg.width= YTPHY_WOL_WIDTH_672MS;
- ret = ytphy_wol_en_cfg(phydev, wol_cfg);
- if (ret < 0)
- return ret;
+ /* for 8010, no definition mii reg 0x04, 0x11, here force 100/full */
+ phydev->speed = SPEED_100;
+ phydev->duplex = DUPLEX_FULL;
- /* Store the device address for the magic packet */
- ret = ytphy_write_ext(phydev, YTPHY_MAGIC_PACKET_MAC_ADDR2,
- ((p_attached_dev->dev_addr[0] << 8) |
- p_attached_dev->dev_addr[1]));
- if (ret < 0)
- return ret;
- ret = ytphy_write_ext(phydev, YTPHY_MAGIC_PACKET_MAC_ADDR1,
- ((p_attached_dev->dev_addr[2] << 8) |
- p_attached_dev->dev_addr[3]));
- if (ret < 0)
- return ret;
- ret = ytphy_write_ext(phydev, YTPHY_MAGIC_PACKET_MAC_ADDR0,
- ((p_attached_dev->dev_addr[4] << 8) |
- p_attached_dev->dev_addr[5]));
- if (ret < 0)
- return ret;
- } else {
- wol_cfg.enable = 0; //disable
- wol_cfg.type= YTPHY_WOL_TYPE_MAX;
- wol_cfg.width= YTPHY_WOL_WIDTH_MAX;
- ret = ytphy_wol_en_cfg(phydev, wol_cfg);
- if (ret < 0)
- return ret;
- }
+ return 0;
+}
- /* Recover to previous register space page */
- ret = ytphy_switch_reg_space(phydev, pre_page);
- if (ret < 0)
- return ret;
+static int yt8010AS_config_init(struct phy_device *phydev)
+{
+ phydev->autoneg = AUTONEG_DISABLE;
return 0;
}
-#endif /*(YTPHY_ENABLE_WOL)*/
-static int yt8521_config_init(struct phy_device *phydev)
+static int yt8512_led_init(struct phy_device *phydev)
{
int ret;
int val;
+ int mask;
- phydev->irq = PHY_POLL;
+ val = ytphy_read_ext(phydev, YT8512_EXTREG_LED0);
+ if (val < 0)
+ return val;
- ytphy_write_ext(phydev, 0xa000, 0);
+ val |= YT8512_LED0_ACT_BLK_IND;
- ret = genphy_config_init(phydev);
+ mask = YT8512_LED0_DIS_LED_AN_TRY | YT8512_LED0_BT_BLK_EN |
+ YT8512_LED0_HT_BLK_EN | YT8512_LED0_COL_BLK_EN |
+ YT8512_LED0_BT_ON_EN;
+ val &= ~mask;
+
+ ret = ytphy_write_ext(phydev, YT8512_EXTREG_LED0, val);
if (ret < 0)
return ret;
- /* disable auto sleep */
- val = ytphy_read_ext(phydev, YT8511_EXT_SLEEP_CTRL);
+ val = ytphy_read_ext(phydev, YT8512_EXTREG_LED1);
if (val < 0)
return val;
- val &= ~YT8521_SLEEP_SW_EN;
+ val |= YT8512_LED1_BT_ON_EN;
- ret = ytphy_write_ext(phydev, YT8511_EXT_SLEEP_CTRL, val);
- if (ret < 0)
- return ret;
+ mask = YT8512_LED1_TXACT_BLK_EN | YT8512_LED1_RXACT_BLK_EN;
+ val &= ~mask;
- /* enable tx delay 450ps per step */
- val = ytphy_read_ext(phydev, 0xa003);
- if (val < 0) {
- printk(KERN_INFO "yt8521_config: read 0xa003 error!\n");
- return val;
- }
+ ret = ytphy_write_ext(phydev, YT8512_LED1_BT_ON_EN, val);
- val &= ~0x3CFF;
- val |= 0x5b;
- ret = ytphy_write_ext(phydev, 0xa003, val);
- if (ret < 0) {
- printk(KERN_INFO "yt8521_config: set 0xa003 error!\n");
- return ret;
- }
+ return ret;
+}
- /* disable rx delay */
- val = ytphy_read_ext(phydev, 0xa001);
- if (val < 0) {
- printk(KERN_INFO "yt8521_config: read 0xa001 error!\n");
- return val;
- }
- val &= ~(1<<8);
- val |= BIT(8);
- ret = ytphy_write_ext(phydev, 0xa001, val);
- if (ret < 0) {
- printk(KERN_INFO "yt8521_config: failed to disable rx_delay!\n");
- return ret;
- }
+static int yt8512_config_init(struct phy_device *phydev)
+{
+ int ret;
+ int val;
- /* enable RXC clock when no wire plug */
- ret = ytphy_write_ext(phydev, 0xa000, 0);
+ ret = ytphy_config_init(phydev);
if (ret < 0)
return ret;
- val = ytphy_read_ext(phydev, YT8511_EXT_CLK_GATE);
+ ret = yt8512_led_init(phydev);
+
+ /* disable auto sleep */
+ val = ytphy_read_ext(phydev, YT8512_EXTREG_SLEEP_CONTROL1);
if (val < 0)
return val;
- val &= ~(1 << 12);
- ret = ytphy_write_ext(phydev, YT8511_EXT_CLK_GATE, val);
+
+ val &= (~BIT(YT8512_EN_SLEEP_SW_BIT));
+
+ ret = ytphy_write_ext(phydev, YT8512_EXTREG_SLEEP_CONTROL1, val);
if (ret < 0)
return ret;
return ret;
}
-/*
- * for fiber mode, there is no 10M speed mode and
- * this function is for this purpose.
- */
-static int yt8521_adjust_status(struct phy_device *phydev, int val, int is_utp)
+static int yt8512_read_status(struct phy_device *phydev)
{
- int speed_mode, duplex;
- int speed = SPEED_UNKNOWN;
+ int ret;
+ int val;
+ int speed, speed_mode, duplex;
+
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ val = phy_read(phydev, REG_PHY_SPEC_STATUS);
+ if (val < 0)
+ return val;
- duplex = (val & YT8521_DUPLEX) >> 13;
- speed_mode = (val & YT8521_SPEED_MODE) >> 14;
+ duplex = (val & YT8512_DUPLEX) >> YT8512_DUPLEX_BIT;
+ speed_mode = (val & YT8512_SPEED_MODE) >> YT8512_SPEED_MODE_BIT;
switch (speed_mode) {
case 0:
- if (is_utp)
- speed = SPEED_10;
+ speed = SPEED_10;
break;
case 1:
speed = SPEED_100;
break;
case 2:
- speed = SPEED_1000;
- break;
case 3:
- break;
default:
speed = SPEED_UNKNOWN;
break;
phydev->speed = speed;
phydev->duplex = duplex;
+
return 0;
}
-static int yt8521_read_status(struct phy_device *phydev)
+int yt8521_soft_reset(struct phy_device *phydev)
{
- int ret;
- volatile int val;
- volatile int link;
- int link_utp = 0;
+ int ret, val;
- /* reading UTP */
- ret = ytphy_write_ext(phydev, 0xa000, 0);
- if (ret < 0)
- return ret;
+ if (YT8521_PHY_MODE_CURR == YT8521_PHY_MODE_UTP) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ ytphy_soft_reset(phydev);
+ }
- val = phy_read(phydev, REG_PHY_SPEC_STATUS);
- if (val < 0)
- return val;
+ if (YT8521_PHY_MODE_CURR == YT8521_PHY_MODE_FIBER) {
+ ytphy_write_ext(phydev, 0xa000, 2);
+ ytphy_soft_reset(phydev);
- link = val & YT8521_LINK_STATUS;
- if (link) {
- link_utp = 1;
- yt8521_adjust_status(phydev, val, 1);
- } else {
- link_utp = 0;
+ ytphy_write_ext(phydev, 0xa000, 0);
}
- if (link_utp) {
- phydev->link = 1;
+ if (YT8521_PHY_MODE_CURR == YT8521_PHY_MODE_POLL) {
+ val = ytphy_read_ext(phydev, 0xa001);
+ ytphy_write_ext(phydev, 0xa001, (val & ~0x8000));
+
ytphy_write_ext(phydev, 0xa000, 0);
- } else {
- phydev->link = 0;
+ ret = ytphy_soft_reset(phydev);
}
return 0;
}
-int yt8521_suspend(struct phy_device *phydev)
+#if GMAC_CLOCK_INPUT_NEEDED
+static int ytphy_mii_rd_ext(struct mii_bus *bus, int phy_id, u32 regnum)
{
-#if !(SYS_WAKEUP_BASED_ON_ETH_PKT)
- int value;
-
- ytphy_write_ext(phydev, 0xa000, 0);
- value = phy_read(phydev, MII_BMCR);
- phy_write(phydev, MII_BMCR, value | BMCR_PDOWN);
+ int ret;
+ int val;
- ytphy_write_ext(phydev, 0xa000, 2);
- value = phy_read(phydev, MII_BMCR);
- phy_write(phydev, MII_BMCR, value | BMCR_PDOWN);
+ ret = bus->write(bus, phy_id, REG_DEBUG_ADDR_OFFSET, regnum);
+ if (ret < 0)
+ return ret;
- ytphy_write_ext(phydev, 0xa000, 0);
-#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/
+ val = bus->read(bus, phy_id, REG_DEBUG_DATA);
- return 0;
+ return val;
}
-int yt8521_resume(struct phy_device *phydev)
+static int ytphy_mii_wr_ext(struct mii_bus *bus
+ int phy_id,
+ u32 regnum,
+ u16 val)
{
-#if !(SYS_WAKEUP_BASED_ON_ETH_PKT)
- int value;
int ret;
- ytphy_write_ext(phydev, 0xa000, 0);
- value = phy_read(phydev, MII_BMCR);
- phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN);
+ ret = bus->write(bus, phy_id, REG_DEBUG_ADDR_OFFSET, regnum);
+ if (ret < 0)
+ return ret;
+
+ ret = bus->write(bus, phy_id, REG_DEBUG_DATA, val);
+
+ return ret;
+}
+
+int yt8511_config_dis_txdelay(struct mii_bus *bus, int phy_id)
+{
+ int ret;
+ int val;
/* disable auto sleep */
- value = ytphy_read_ext(phydev, YT8511_EXT_SLEEP_CTRL);
- if (value < 0)
- return value;
+ val = ytphy_mii_rd_ext(bus, phy_id, 0x27);
+ if (val < 0)
+ return val;
- value &= ~YT8521_SLEEP_SW_EN;
- ret = ytphy_write_ext(phydev, YT8511_EXT_SLEEP_CTRL, value);
+ val &= (~BIT(15));
+
+ ret = ytphy_mii_wr_ext(bus, phy_id, 0x27, val);
if (ret < 0)
return ret;
/* enable RXC clock when no wire plug */
- value = ytphy_read_ext(phydev, YT8511_EXT_CLK_GATE);
- if (value < 0)
- return value;
- value &= ~(1 << 12);
- ret = ytphy_write_ext(phydev, YT8511_EXT_CLK_GATE, value);
- if (ret < 0)
- return ret;
+ val = ytphy_mii_rd_ext(bus, phy_id, 0xc);
+ if (val < 0)
+ return val;
- ytphy_write_ext(phydev, 0xa000, 2);
- value = phy_read(phydev, MII_BMCR);
- phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN);
+ /* ext reg 0xc b[7:4]
+ * Tx Delay time = 150ps * N - 250ps
+ */
+ val &= ~(0xf << 4);
+ ret = ytphy_mii_wr_ext(bus, phy_id, 0xc, val);
- ytphy_write_ext(phydev, 0xa000, 0);
+ return ret;
+}
-#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/
+int yt8511_config_out_125m(struct mii_bus *bus, int phy_id)
+{
+ int ret;
+ int val;
- return 0;
-}
+ /* disable auto sleep */
+ val = ytphy_mii_rd_ext(bus, phy_id, 0x27);
+ if (val < 0)
+ return val;
-static struct phy_driver motorcomm_phy_drvs[] = {
- {
- PHY_ID_MATCH_EXACT(PHY_ID_YT8511),
- .name = "YT8511 Gigabit Ethernet",
- .config_init = yt8511_config_init,
- .suspend = genphy_suspend,
- .resume = genphy_resume,
- .read_page = yt8511_read_page,
- .write_page = yt8511_write_page,
- },
+ val &= (~BIT(15));
+
+ ret = ytphy_mii_wr_ext(bus, phy_id, 0x27, val);
+ if (ret < 0)
+ return ret;
+
+ /* enable RXC clock when no wire plug */
+ val = ytphy_mii_rd_ext(bus, phy_id, 0xc);
+ if (val < 0)
+ return val;
+
+ /* ext reg 0xc.b[2:1]
+ * 00-----25M from pll;
+ * 01---- 25M from xtl;(default)
+ * 10-----62.5M from pll;
+ * 11----125M from pll(here set to this value)
+ */
+ val |= (3 << 1);
+ ret = ytphy_mii_wr_ext(bus, phy_id, 0xc, val);
+
+#ifdef YT_8511_INIT_TO_MASTER
+ /* for customer, please enable it based on demand.
+ * configure to master
+ */
+
+ /* master/slave config reg*/
+ val = bus->read(bus, phy_id, 0x9);
+ /* to be manual config and force to be master */
+ val |= (0x3 << 11);
+ /* take effect until phy soft reset */
+ ret = bus->write(bus, phy_id, 0x9, val);
+ if (ret < 0)
+ return ret;
+#endif
+
+ return ret;
+}
+
+static int yt8511_config_init(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = ytphy_config_init(phydev);
+
+ netdev_info(phydev->attached_dev, "%s done, phy addr: %d\n",
+ __func__, phydev->mdio.addr);
+
+ return ret;
+}
+#endif /* GMAC_CLOCK_INPUT_NEEDED */
+
+#if (YTPHY_WOL_FEATURE_ENABLE)
+static int ytphy_switch_reg_space(struct phy_device *phydev, int space)
+{
+ int ret;
+
+ if (space == YTPHY_REG_SPACE_UTP)
+ ret = ytphy_write_ext(phydev, 0xa000, 0);
+ else
+ ret = ytphy_write_ext(phydev, 0xa000, 2);
+
+ return ret;
+}
+
+static int ytphy_wol_feature_enable_cfg(struct phy_device *phydev,
+ struct ytphy_wol_feature_cfg wol_cfg)
+{
+ int ret = 0;
+ int val = 0;
+
+ val = ytphy_read_ext(phydev, YTPHY_WOL_FEATURE_REG_CFG);
+ if (val < 0)
+ return val;
+
+ if (wol_cfg.enable) {
+ val |= YTPHY_WOL_FEATURE_ENABLE_CFG;
+
+ if (wol_cfg.type == YTPHY_WOL_FEATURE_LEVEL_TRIGGER) {
+ val &= ~YTPHY_WOL_FEATURE_TYPE_CFG;
+ val &= ~YTPHY_WOL_FEATURE_INTR_SEL_CFG;
+ } else if (wol_cfg.type == YTPHY_WOL_FEATURE_PULSE_TRIGGER) {
+ val |= YTPHY_WOL_FEATURE_TYPE_CFG;
+ val |= YTPHY_WOL_FEATURE_INTR_SEL_CFG;
+
+ if (wol_cfg.width == YTPHY_WOL_FEATURE_84MS_PULSE_WIDTH) {
+ val &= ~YTPHY_WOL_FEATURE_WIDTH1_CFG;
+ val &= ~YTPHY_WOL_FEATURE_WIDTH2_CFG;
+ } else if (wol_cfg.width == YTPHY_WOL_FEATURE_168MS_PULSE_WIDTH) {
+ val |= YTPHY_WOL_FEATURE_WIDTH1_CFG;
+ val &= ~YTPHY_WOL_FEATURE_WIDTH2_CFG;
+ } else if (wol_cfg.width == YTPHY_WOL_FEATURE_336MS_PULSE_WIDTH) {
+ val &= ~YTPHY_WOL_FEATURE_WIDTH1_CFG;
+ val |= YTPHY_WOL_FEATURE_WIDTH2_CFG;
+ } else if (wol_cfg.width == YTPHY_WOL_FEATURE_672MS_PULSE_WIDTH) {
+ val |= YTPHY_WOL_FEATURE_WIDTH1_CFG;
+ val |= YTPHY_WOL_FEATURE_WIDTH2_CFG;
+ }
+ }
+ } else {
+ val &= ~YTPHY_WOL_FEATURE_ENABLE_CFG;
+ val &= ~YTPHY_WOL_FEATURE_INTR_SEL_CFG;
+ }
+
+ ret = ytphy_write_ext(phydev, YTPHY_WOL_FEATURE_REG_CFG, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void ytphy_wol_feature_get(struct phy_device *phydev,
+ struct ethtool_wolinfo *wol)
+{
+ int val = 0;
+
+ wol->supported = WAKE_MAGIC;
+ wol->wolopts = 0;
+
+ val = ytphy_read_ext(phydev, YTPHY_WOL_FEATURE_REG_CFG);
+ if (val < 0)
+ return;
+
+ if (val & YTPHY_WOL_FEATURE_ENABLE_CFG)
+ wol->wolopts |= WAKE_MAGIC;
+
+ //return;
+}
+
+static int ytphy_wol_feature_set(struct phy_device *phydev,
+ struct ethtool_wolinfo *wol)
+{
+ int ret, curr_reg_space, val;
+ struct ytphy_wol_feature_cfg wol_cfg;
+ struct net_device *p_attached_dev = phydev->attached_dev;
+
+ memset(&wol_cfg, 0, sizeof(struct ytphy_wol_feature_cfg));
+ curr_reg_space = ytphy_read_ext(phydev, 0xa000);
+ if (curr_reg_space < 0)
+ return curr_reg_space;
+
+ /* Switch to phy UTP page */
+ ret = ytphy_switch_reg_space(phydev, YTPHY_REG_SPACE_UTP);
+ if (ret < 0)
+ return ret;
+
+ if (wol->wolopts & WAKE_MAGIC) {
+ /* Enable the WOL feature interrupt */
+ val = phy_read(phydev, YTPHY_UTP_INTR_REG);
+ val |= YTPHY_WOL_FEATURE_INTR;
+ ret = phy_write(phydev, YTPHY_UTP_INTR_REG, val);
+ if (ret < 0)
+ return ret;
+
+ /* Set the WOL feature config */
+ wol_cfg.enable = true;
+ wol_cfg.type = YTPHY_WOL_FEATURE_PULSE_TRIGGER;
+ wol_cfg.width = YTPHY_WOL_FEATURE_672MS_PULSE_WIDTH;
+ ret = ytphy_wol_feature_enable_cfg(phydev, wol_cfg);
+ if (ret < 0)
+ return ret;
+
+ /* Store the device address for the magic packet */
+ ret = ytphy_write_ext(phydev, YTPHY_WOL_FEATURE_MACADDR2_4_MAGIC_PACKET,
+ ((p_attached_dev->dev_addr[0] << 8) |
+ p_attached_dev->dev_addr[1]));
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, YTPHY_WOL_FEATURE_MACADDR1_4_MAGIC_PACKET,
+ ((p_attached_dev->dev_addr[2] << 8) |
+ p_attached_dev->dev_addr[3]));
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, YTPHY_WOL_FEATURE_MACADDR0_4_MAGIC_PACKET,
+ ((p_attached_dev->dev_addr[4] << 8) |
+ p_attached_dev->dev_addr[5]));
+ if (ret < 0)
+ return ret;
+ } else {
+ wol_cfg.enable = false;
+ wol_cfg.type = YTPHY_WOL_FEATURE_TRIGGER_TYPE_MAX;
+ wol_cfg.width = YTPHY_WOL_FEATURE_PULSE_WIDTH_MAX;
+ ret = ytphy_wol_feature_enable_cfg(phydev, wol_cfg);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Recover to previous register space page */
+ ret = ytphy_switch_reg_space(phydev, curr_reg_space);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+#endif /*(YTPHY_WOL_FEATURE_ENABLE)*/
+
+static int yt8521_hw_strap_polling(struct phy_device *phydev)
+{
+ int val = 0;
+
+ val = ytphy_read_ext(phydev, 0xa001) & 0x7;
+ switch (val) {
+ case 1:
+ case 4:
+ case 5:
+ return YT8521_PHY_MODE_FIBER;
+ case 2:
+ case 6:
+ case 7:
+ return YT8521_PHY_MODE_POLL;
+ case 3:
+ case 0:
+ default:
+ return YT8521_PHY_MODE_UTP;
+ }
+}
+
+static int ytphy_of_config(struct phy_device *phydev)
+{
+ const struct device_node *of_node;
+ const struct device *dev;
+ u32 val;
+ u32 cfg;
+ int ret;
+ int i = 0;
+
+ dev = &phydev->mdio.dev;
+ do {
+ of_node = dev->of_node;
+ dev = dev->parent;
+ if (i++ > 5) {
+ phydev_err(phydev, "Get of node timeout\n");
+ return -EINVAL;
+ }
+ } while (!of_node && dev);
+
+ of_node = of_node->child;
+ if (of_node) {
+ ret = of_property_read_u32(of_node, ytphy_rxden_grp[0].name, &cfg);
+ if (!ret) {
+ val = ytphy_read_ext(phydev, YTPHY_EXTREG_CHIP_CONFIG);
+
+ /*check the cfg overflow or not*/
+ cfg = (cfg > ((1 << ytphy_rxden_grp[0].size) - 1)) ?
+ ((1 << ytphy_rxden_grp[0].size) - 1) : cfg;
+ val = bitfield_replace(val, ytphy_rxden_grp[0].off,
+ ytphy_rxden_grp[0].size, cfg);
+ ytphy_write_ext(phydev, YTPHY_EXTREG_CHIP_CONFIG, val);
+ }
+
+ val = ytphy_read_ext(phydev, YTPHY_EXTREG_RGMII_CONFIG1);
+ for (i = 0; i < ARRAY_SIZE(ytphy_rxtxd_grp); i++) {
+ ret = of_property_read_u32(of_node, ytphy_rxtxd_grp[i].name, &cfg);
+ if (!ret) {
+ cfg = (cfg != -1) ? cfg : ytphy_rxtxd_grp[i].dflt;
+
+ /*check the cfg overflow or not*/
+ cfg = (cfg > ((1 << ytphy_rxtxd_grp[i].size) - 1)) ?
+ ((1 << ytphy_rxtxd_grp[i].size) - 1) : cfg;
+
+ val = bitfield_replace(val, ytphy_rxtxd_grp[i].off,
+ ytphy_rxtxd_grp[i].size, cfg);
+ }
+ }
+ return ytphy_write_ext(phydev, YTPHY_EXTREG_RGMII_CONFIG1, val);
+ }
+
+ phydev_err(phydev, "Get of node fail\n");
+ return -EINVAL;
+}
+
+static int yt8521_config_init(struct phy_device *phydev)
+{
+ int ret;
+ int val, hw_strap_mode;
+
+#if (YTPHY_WOL_FEATURE_ENABLE)
+ struct ethtool_wolinfo wol;
+
+ /* set phy wol enable */
+ memset(&wol, 0x0, sizeof(struct ethtool_wolinfo));
+ wol.wolopts |= WAKE_MAGIC;
+ ytphy_wol_feature_set(phydev, &wol);
+#endif
+
+ phydev->irq = PHY_POLL;
+ /* NOTE: this function should not be
+ * called more than one for each chip.
+ */
+ hw_strap_mode = ytphy_read_ext(phydev, 0xa001) & 0x7;
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+ ret = ytphy_config_init(phydev);
+
+ if (ret < 0)
+ return ret;
+
+ /* disable auto sleep */
+ val = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1);
+ if (val < 0)
+ return val;
+
+ val &= (~BIT(YT8521_EN_SLEEP_SW_BIT));
+
+ ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, val);
+ if (ret < 0)
+ return ret;
+
+ /*set delay config*/
+ ret = ytphy_of_config(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* enable RXC clock when no wire plug */
+ val = ytphy_read_ext(phydev, 0xc);
+ if (val < 0)
+ return val;
+ val &= ~(1 << 12);
+ ret = ytphy_write_ext(phydev, 0xc, val);
+ if (ret < 0)
+ return ret;
+
+ netdev_info(phydev->attached_dev,
+ "%s done, phy addr: %d, strap mode = %d, polling mode = %d\n",
+ __func__, phydev->mdio.addr, hw_strap_mode,
+ yt8521_hw_strap_polling(phydev));
+
+ return ret;
+}
+
+/* for fiber mode, there is no 10M speed mode and
+ * this function is for this purpose.
+ */
+static int yt8521_adjust_status(struct phy_device *phydev, int val, int is_utp)
+{
+ int speed_mode, duplex;
+ int speed = SPEED_UNKNOWN;
+
+ if (is_utp)
+ duplex = (val & YT8512_DUPLEX) >> YT8521_DUPLEX_BIT;
+ else
+ duplex = 1;
+ speed_mode = (val & YT8521_SPEED_MODE) >> YT8521_SPEED_MODE_BIT;
+ switch (speed_mode) {
+ case 0:
+ if (is_utp)
+ speed = SPEED_10;
+ break;
+ case 1:
+ speed = SPEED_100;
+ break;
+ case 2:
+ speed = SPEED_1000;
+ break;
+ case 3:
+ break;
+ default:
+ speed = SPEED_UNKNOWN;
+ break;
+ }
+
+ phydev->speed = speed;
+ phydev->duplex = duplex;
+
+ return 0;
+}
+
+/* for fiber mode, when speed is 100M, there is no definition for
+ * autonegotiation, and this function handles this case and return
+ * 1 per linux kernel's polling.
+ */
+int yt8521_aneg_done(struct phy_device *phydev)
+{
+ int link_fiber = 0, link_utp = 0;
+
+ /* reading Fiber */
+ ytphy_write_ext(phydev, 0xa000, 2);
+ link_fiber = !!(phy_read(phydev, REG_PHY_SPEC_STATUS) &
+ (BIT(YT8521_LINK_STATUS_BIT)));
+
+ /* reading UTP */
+ ytphy_write_ext(phydev, 0xa000, 0);
+ if (!link_fiber)
+ link_utp = !!(phy_read(phydev, REG_PHY_SPEC_STATUS) &
+ (BIT(YT8521_LINK_STATUS_BIT)));
+
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, link_fiber: %d, link_utp: %d\n",
+ __func__, phydev->mdio.addr, link_fiber, link_utp);
+
+ return !!(link_fiber | link_utp);
+}
+
+static int yt8521_read_status(struct phy_device *phydev)
+{
+ int ret;
+ int val;
+ int yt8521_fiber_latch_val;
+ int yt8521_fiber_curr_val;
+ int link;
+ int link_utp = 0, link_fiber = 0;
+
+ if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) {
+ /* reading UTP */
+ ret = ytphy_write_ext(phydev, 0xa000, 0);
+ if (ret < 0)
+ return ret;
+
+ val = phy_read(phydev, REG_PHY_SPEC_STATUS);
+ if (val < 0)
+ return val;
+
+ link = val & (BIT(YT8521_LINK_STATUS_BIT));
+ if (link) {
+ link_utp = 1;
+ yt8521_adjust_status(phydev, val, 1);
+ } else {
+ link_utp = 0;
+ }
+ } //(YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER)
+
+ if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_UTP) {
+ /* reading Fiber */
+ ret = ytphy_write_ext(phydev, 0xa000, 2);
+ if (ret < 0)
+ return ret;
+
+ val = phy_read(phydev, REG_PHY_SPEC_STATUS);
+ if (val < 0)
+ return val;
+
+ //note: below debug information is used to check multiple PHy ports.
+
+ /* for fiber, from 1000m to 100m, there is not link down from 0x11,
+ * and check reg 1 to identify such case this is important for Linux
+ * kernel for that, missing linkdown event will cause problem.
+ */
+ yt8521_fiber_latch_val = phy_read(phydev, MII_BMSR);
+ yt8521_fiber_curr_val = phy_read(phydev, MII_BMSR);
+ link = val & (BIT(YT8521_LINK_STATUS_BIT));
+ if (link && yt8521_fiber_latch_val != yt8521_fiber_curr_val) {
+ link = 0;
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, fiber link down detect,"
+ "latch = %04x, curr = %04x\n",
+ __func__, phydev->mdio.addr,
+ yt8521_fiber_latch_val,
+ yt8521_fiber_curr_val);
+ }
+
+ if (link) {
+ link_fiber = 1;
+ yt8521_adjust_status(phydev, val, 0);
+ } else {
+ link_fiber = 0;
+ }
+ } //(YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_UTP)
+
+ if (link_utp || link_fiber) {
+ if (phydev->link == 0)
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, link up, media: %s,"
+ "mii reg 0x11 = 0x%x\n",
+ __func__, phydev->mdio.addr,
+ (link_utp && link_fiber) ?
+ "UNKONWN MEDIA" :
+ (link_utp ? "UTP" : "Fiber"),
+ (unsigned int)val);
+ phydev->link = 1;
+ } else {
+ if (phydev->link == 1)
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, link down\n",
+ __func__, phydev->mdio.addr);
+ phydev->link = 0;
+ }
+
+ if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) { //utp or combo
+ if (link_fiber)
+ ytphy_write_ext(phydev, 0xa000, 2);
+ if (link_utp)
+ ytphy_write_ext(phydev, 0xa000, 0);
+ }
+ return 0;
+}
+
+int yt8521_suspend(struct phy_device *phydev)
+{
+#if !(SYS_WAKEUP_BASED_ON_ETH_PKT)
+ int value;
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+ value = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, value | BMCR_PDOWN);
+
+ ytphy_write_ext(phydev, 0xa000, 2);
+ value = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, value | BMCR_PDOWN);
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+
+#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/
+
+ return 0;
+}
+
+int yt8521_resume(struct phy_device *phydev)
+{
+ int value, ret;
+
+ /* disable auto sleep */
+ value = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1);
+ if (value < 0)
+ return value;
+
+ value &= (~BIT(YT8521_EN_SLEEP_SW_BIT));
+
+ ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, value);
+ if (ret < 0)
+ return ret;
+
+#if !(SYS_WAKEUP_BASED_ON_ETH_PKT)
+
+ if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ value = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN);
+ }
+
+ if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_UTP) {
+ ytphy_write_ext(phydev, 0xa000, 2);
+ value = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN);
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+ }
+
+#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/
+
+ return 0;
+}
+
+static int yt8531S_config_init(struct phy_device *phydev)
+{
+ int ret = 0;
+
+#if (YTPHY8531A_XTAL_INIT)
+ ret = yt8531a_xtal_init(phydev);
+ if (ret < 0)
+ return ret;
+#endif
+
+ ret = yt8521_config_init(phydev);
+
+ return ret;
+}
+
+static int yt8531_config_init(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = 0;
+
+ /*set delay config*/
+ ret = ytphy_of_config(phydev);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+int yt8618_soft_reset(struct phy_device *phydev)
+{
+ int ret;
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+ ret = ytphy_soft_reset(phydev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int yt8614_soft_reset(struct phy_device *phydev)
+{
+ int ret;
+
+ /* qsgmii */
+ ytphy_write_ext(phydev, 0xa000, 2);
+ ret = ytphy_soft_reset(phydev);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ return ret;
+ }
+
+ /* sgmii */
+ ytphy_write_ext(phydev, 0xa000, 3);
+ ret = ytphy_soft_reset(phydev);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ return ret;
+ }
+
+ /* utp */
+ ytphy_write_ext(phydev, 0xa000, 0);
+ ret = ytphy_soft_reset(phydev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int yt8618_config_init(struct phy_device *phydev)
+{
+ int ret;
+ int val;
+ unsigned int retries = 12;
+#if (YTPHY_861X_ABC_VER)
+ int port = 0;
+#endif
+
+ phydev->irq = PHY_POLL;
+
+#if (YTPHY_861X_ABC_VER)
+ port = yt8614_get_port_from_phydev(phydev);
+#endif
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+ ret = ytphy_config_init(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* for utp to optimize signal */
+ ret = ytphy_write_ext(phydev, 0x41, 0x33);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x42, 0x66);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x43, 0xaa);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x44, 0xd0d);
+ if (ret < 0)
+ return ret;
+
+#if (YTPHY_861X_ABC_VER)
+ if (port == 2 || port == 5) {
+ ret = ytphy_write_ext(phydev, 0x57, 0x2929);
+ if (ret < 0)
+ return ret;
+ }
+#endif
+
+ val = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, val | BMCR_RESET);
+ do {
+ msleep(50);
+ ret = phy_read(phydev, MII_BMCR);
+ if (ret < 0)
+ return ret;
+ } while ((ret & BMCR_RESET) && --retries);
+ if (ret & BMCR_RESET)
+ return -ETIMEDOUT;
+
+ /* for QSGMII optimization */
+ ytphy_write_ext(phydev, 0xa000, 0x02);
+
+ ret = ytphy_write_ext(phydev, 0x3, 0x4F80);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ return ret;
+ }
+ ret = ytphy_write_ext(phydev, 0xe, 0x4F80);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ return ret;
+ }
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+
+ netdev_info(phydev->attached_dev, "%s done, phy addr: %d\n",
+ __func__, phydev->mdio.addr);
+ return ret;
+}
+
+static int yt8614_hw_strap_polling(struct phy_device *phydev)
+{
+ int val = 0;
+
+ /* b3:0 are work mode, but b3 is always 1 */
+ val = ytphy_read_ext(phydev, 0xa007) & 0xf;
+ switch (val) {
+ case 8:
+ case 12:
+ case 13:
+ case 15:
+ return (YT_PHY_MODE_FIBER | YT_PHY_MODE_UTP);
+ case 14:
+ case 11:
+ return YT_PHY_MODE_FIBER;
+ case 9:
+ case 10:
+ default:
+ return YT_PHY_MODE_UTP;
+ }
+}
+
+#if (YTPHY_861X_ABC_VER)
+static int yt8614_get_port_from_phydev(struct phy_device *phydev)
+{
+ int tmp = ytphy_read_ext(phydev, 0xa0ff);
+ int phy_addr = 0;
+
+ phy_addr = (unsigned int)phydev->mdio.addr;
+
+ if ((phy_addr - tmp) < 0) {
+ ytphy_write_ext(phydev, 0xa0ff, phy_addr);
+ tmp = phy_addr;
+ }
+
+ return (phy_addr - tmp);
+}
+#endif
+
+static int yt8614_config_init(struct phy_device *phydev)
+{
+ int ret = 0;
+ int val, hw_strap_mode;
+ unsigned int retries = 12;
+#if (YTPHY_861X_ABC_VER)
+ int port = 0;
+#endif
+ phydev->irq = PHY_POLL;
+
+ /* NOTE: this function should not be called more than one for each chip. */
+ hw_strap_mode = ytphy_read_ext(phydev, 0xa007) & 0xf;
+
+#if (YTPHY_861X_ABC_VER)
+ port = yt8614_get_port_from_phydev(phydev);
+#endif
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+ ret = ytphy_config_init(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* for utp to optimize signal */
+ ret = ytphy_write_ext(phydev, 0x41, 0x33);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x42, 0x66);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x43, 0xaa);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x44, 0xd0d);
+ if (ret < 0)
+ return ret;
+
+#if (YTPHY_861X_ABC_VER)
+ if (port == 2) {
+ ret = ytphy_write_ext(phydev, 0x57, 0x2929);
+ if (ret < 0)
+ return ret;
+ }
+#endif
+
+ /* soft reset to take config effect */
+ val = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, val | BMCR_RESET);
+ do {
+ msleep(50);
+ ret = phy_read(phydev, MII_BMCR);
+ if (ret < 0)
+ return ret;
+ } while ((ret & BMCR_RESET) && --retries);
+ if (ret & BMCR_RESET)
+ return -ETIMEDOUT;
+
+ /* for QSGMII optimization */
+ ytphy_write_ext(phydev, 0xa000, 0x02);
+ ret = ytphy_write_ext(phydev, 0x3, 0x4F80);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ return ret;
+ }
+ ret = ytphy_write_ext(phydev, 0xe, 0x4F80);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ return ret;
+ }
+
+ /* for SGMII optimization */
+ ytphy_write_ext(phydev, 0xa000, 0x03);
+ ret = ytphy_write_ext(phydev, 0x3, 0x2420);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ return ret;
+ }
+ ret = ytphy_write_ext(phydev, 0xe, 0x24a0);
+ if (ret < 0) {
+ ytphy_write_ext(phydev, 0xa000, 0);
+ return ret;
+ }
+
+ /* back up to utp*/
+ ytphy_write_ext(phydev, 0xa000, 0);
+ netdev_info(phydev->attached_dev,
+ "%s done, phy addr: %d, chip mode: %d\n",
+ __func__, phydev->mdio.addr, hw_strap_mode);
+
+ return ret;
+}
+
+int yt8618_aneg_done(struct phy_device *phydev)
+{
+ return genphy_aneg_done(phydev);
+}
+
+int yt8614_aneg_done(struct phy_device *phydev)
+{
+ int link_fiber = 0, link_utp = 0;
+
+ /* reading Fiber */
+ ytphy_write_ext(phydev, 0xa000, 3);
+ link_fiber = !!(phy_read(phydev, REG_PHY_SPEC_STATUS) &
+ (BIT(YT8521_LINK_STATUS_BIT)));
+
+ /* reading UTP */
+ ytphy_write_ext(phydev, 0xa000, 0);
+ if (!link_fiber)
+ link_utp = !!(phy_read(phydev, REG_PHY_SPEC_STATUS) &
+ (BIT(YT8521_LINK_STATUS_BIT)));
+ return !!(link_fiber | link_utp);
+}
+
+static int yt8614_read_status(struct phy_device *phydev)
+{
+ int ret;
+ int val, yt8614_fiber_latch_val, yt8614_fiber_curr_val;
+ int link;
+ int link_utp = 0, link_fiber = 0;
+
+ if (YT8614_PHY_MODE_CURR & YT_PHY_MODE_UTP) {
+ /* switch to utp and reading regs */
+ ret = ytphy_write_ext(phydev, 0xa000, 0);
+ if (ret < 0)
+ return ret;
+
+ val = phy_read(phydev, REG_PHY_SPEC_STATUS);
+ if (val < 0)
+ return val;
+
+ link = val & (BIT(YT8521_LINK_STATUS_BIT));
+ if (link) {
+ if (phydev->link == 0)
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, link up,"
+ "UTP, reg 0x11 = 0x%x\n",
+ __func__, phydev->mdio.addr,
+ (unsigned int)val);
+ link_utp = 1;
+ // here is same as 8521 and re-use the function;
+ yt8521_adjust_status(phydev, val, 1);
+ } else {
+ link_utp = 0;
+ }
+ }
+
+ if (YT8614_PHY_MODE_CURR & YT_PHY_MODE_FIBER) {
+ /* reading Fiber/sgmii */
+ ret = ytphy_write_ext(phydev, 0xa000, 3);
+ if (ret < 0)
+ return ret;
+
+ val = phy_read(phydev, REG_PHY_SPEC_STATUS);
+ if (val < 0)
+ return val;
+
+ /* for fiber, from 1000m to 100m, there is not link down from 0x11,
+ * and check reg 1 to identify such case
+ */
+ yt8614_fiber_latch_val = phy_read(phydev, MII_BMSR);
+ yt8614_fiber_curr_val = phy_read(phydev, MII_BMSR);
+ link = val & (BIT(YT8521_LINK_STATUS_BIT));
+ if (link && yt8614_fiber_latch_val != yt8614_fiber_curr_val) {
+ link = 0;
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, fiber link down detect,"
+ "latch = %04x, curr = %04x\n",
+ __func__, phydev->mdio.addr,
+ yt8614_fiber_latch_val,
+ yt8614_fiber_curr_val);
+ }
+
+ if (link) {
+ if (phydev->link == 0)
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, link up, Fiber,"
+ "reg 0x11 = 0x%x\n",
+ __func__, phydev->mdio.addr,
+ (unsigned int)val);
+ link_fiber = 1;
+ yt8521_adjust_status(phydev, val, 0);
+ } else {
+ link_fiber = 0;
+ }
+ }
+
+ if (link_utp || link_fiber) {
+ if (phydev->link == 0)
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, link up, media %s\n",
+ __func__, phydev->mdio.addr,
+ (link_utp && link_fiber) ?
+ "both UTP and Fiber" :
+ (link_utp ? "UTP" : "Fiber"));
+ phydev->link = 1;
+ } else {
+ if (phydev->link == 1)
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, link down\n",
+ __func__, phydev->mdio.addr);
+ phydev->link = 0;
+ }
+
+ if (YT8614_PHY_MODE_CURR & YT_PHY_MODE_UTP) {
+ if (link_utp)
+ ytphy_write_ext(phydev, 0xa000, 0);
+ }
+ return 0;
+}
+
+static int yt8618_read_status(struct phy_device *phydev)
+{
+ int ret;
+ /* maybe for 8614 yt8521_fiber_latch_val, yt8521_fiber_curr_val; */
+ int val;
+ int link;
+ int link_utp = 0, link_fiber = 0;
+
+ /* switch to utp and reading regs */
+ ret = ytphy_write_ext(phydev, 0xa000, 0);
+ if (ret < 0)
+ return ret;
+
+ val = phy_read(phydev, REG_PHY_SPEC_STATUS);
+ if (val < 0)
+ return val;
+
+ link = val & (BIT(YT8521_LINK_STATUS_BIT));
+ if (link) {
+ link_utp = 1;
+ yt8521_adjust_status(phydev, val, 1);
+ } else {
+ link_utp = 0;
+ }
+
+ if (link_utp || link_fiber)
+ phydev->link = 1;
+ else
+ phydev->link = 0;
+
+ return 0;
+}
+
+int yt8618_suspend(struct phy_device *phydev)
+{
+#if !(SYS_WAKEUP_BASED_ON_ETH_PKT)
+ int value;
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+ value = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, value | BMCR_PDOWN);
+
+#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/
+
+ return 0;
+}
+
+int yt8618_resume(struct phy_device *phydev)
+{
+#if !(SYS_WAKEUP_BASED_ON_ETH_PKT)
+ int value;
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+ value = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN);
+
+#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/
+
+ return 0;
+}
+
+int yt8614_suspend(struct phy_device *phydev)
+{
+#if !(SYS_WAKEUP_BASED_ON_ETH_PKT)
+ int value;
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+ value = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, value | BMCR_PDOWN);
+
+ ytphy_write_ext(phydev, 0xa000, 3);
+ value = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, value | BMCR_PDOWN);
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+
+#endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/
+
+ return 0;
+}
+
+int yt8614_resume(struct phy_device *phydev)
+{
+#if !(SYS_WAKEUP_BASED_ON_ETH_PKT)
+ int value;
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+ value = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN);
+
+ ytphy_write_ext(phydev, 0xa000, 3);
+ value = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN);
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+
+#endif /* !(SYS_WAKEUP_BASED_ON_ETH_PKT) */
+
+ return 0;
+}
+
+int yt8821_soft_reset(struct phy_device *phydev)
+{
+ int ret, val;
+
+ val = ytphy_read_ext(phydev, 0xa001);
+ ytphy_write_ext(phydev, 0xa001, (val & ~0x8000));
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+ ret = ytphy_soft_reset(phydev);
+
+ return ret;
+}
+
+static int yt8821_init(struct phy_device *phydev)
+{
+ int ret = 0;
+ int val = 0;
+
+ /* sds pll cfg */
+ ret = ytphy_write_ext(phydev, 0xa050, 0x1000);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0xa000, 0x2);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x23, 0x47a1);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0xbd, 0x3547);
+ if (ret < 0)
+ return ret;
+
+ /* wait 1s */
+ msleep(1000);
+
+ /* calibration dcc */
+ ret = ytphy_write_ext(phydev, 0xbd, 0xa547);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x29, 0x3003);
+ if (ret < 0)
+ return ret;
+
+ /* sds driver swing */
+ ret = ytphy_write_ext(phydev, 0x25, 0x788);
+ if (ret < 0)
+ return ret;
+
+ /* phy cfg */
+ ret = ytphy_write_ext(phydev, 0xa000, 0x0);
+ if (ret < 0)
+ return ret;
+
+ /* phy template cfg */
+ ret = ytphy_write_ext(phydev, 0x471, 0x4545);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x476, 0x4848);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x477, 0x4848);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x478, 0x4848);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x479, 0x4848);
+ if (ret < 0)
+ return ret;
+
+ /* calibrate phy lc pll */
+ ret = ytphy_write_ext(phydev, 0x600, 0x2300);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x8, 0x8041);
+ if (ret < 0)
+ return ret;
+
+ /* prm_small_lng/med */
+ ret = ytphy_write_ext(phydev, 0x388, 0x90);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x387, 0x90);
+ if (ret < 0)
+ return ret;
+
+ /* echo_delay_cfg */
+ ret = ytphy_write_ext(phydev, 0x3, 0xa026);
+ if (ret < 0)
+ return ret;
+
+ /* pbo setting */
+ ret = ytphy_write_ext(phydev, 0x47e, 0x3535);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x47f, 0x3535);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x480, 0x3535);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x481, 0x3535);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x483, 0x2a2a);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x484, 0x2a2a);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x485, 0x2a2a);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x486, 0x2a2a);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x488, 0x2121);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x489, 0x2121);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x48a, 0x2121);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x48b, 0x2121);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x48d, 0x1a1a);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x48e, 0x1a1a);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x48f, 0x1a1a);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x490, 0x1a1a);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x492, 0x1515);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x493, 0x1515);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x494, 0x1515);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x495, 0x1515);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x497, 0x1111);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x498, 0x1111);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x499, 0x1111);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x49a, 0x1111);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x49c, 0x0d0d);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x49d, 0x0d0d);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x49e, 0x0d0d);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0x49f, 0x0d0d);
+ if (ret < 0)
+ return ret;
+ ret = ytphy_write_ext(phydev, 0xa052, 0x7);
+ if (ret < 0)
+ return ret;
+
+ /* fast link down cfg */
+ ret = ytphy_write_ext(phydev, 0x355, 0x7d07);
+ if (ret < 0)
+ return ret;
+
+ /* soft reset */
+ val = phy_read(phydev, MII_BMCR);
+ if (val < 0)
+ return val;
+ ret = phy_write(phydev, MII_BMCR, val | BMCR_RESET);
+
+ return ret;
+}
+
+static int yt8821_config_init(struct phy_device *phydev)
+{
+ int ret;
+ int val, hw_strap_mode;
+
+ phydev->irq = PHY_POLL;
+
+ /* NOTE: this function should not be called more than one for each chip. */
+ hw_strap_mode = ytphy_read_ext(phydev, 0xa001) & 0x7;
+
+ ytphy_write_ext(phydev, 0xa000, 0);
+ ret = ytphy_config_init(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = yt8821_init(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* disable auto sleep */
+ val = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1);
+ if (val < 0)
+ return val;
+
+ val &= (~BIT(YT8521_EN_SLEEP_SW_BIT));
+
+ ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, val);
+ if (ret < 0)
+ return ret;
+
+ netdev_info(phydev->attached_dev,
+ "%s done, phy addr: %d, strap mode = %d\n",
+ __func__, phydev->mdio.addr, hw_strap_mode);
+
+ return ret;
+}
+
+/* for fiber mode, there is no 10M speed mode and
+ * this function is for this purpose.
+ */
+static int yt8821_adjust_status(struct phy_device *phydev, int val, int is_utp)
+{
+ int speed_mode, duplex;
+ int speed_mode_bit15_14, speed_mode_bit9;
+ int speed = SPEED_UNKNOWN;
+
+ if (is_utp)
+ duplex = (val & YT8512_DUPLEX) >> YT8521_DUPLEX_BIT;
+ else
+ duplex = 1;
+
+ /* Bit9-Bit15-Bit14 speed mode 100---2.5G; 010---1000M; 001---100M; 000---10M */
+ speed_mode_bit15_14 = (val & YT8521_SPEED_MODE) >> YT8521_SPEED_MODE_BIT;
+ speed_mode_bit9 = (val & BIT(9)) >> 9;
+ speed_mode = (speed_mode_bit9 << 2) | speed_mode_bit15_14;
+ switch (speed_mode) {
+ case 0:
+ if (is_utp)
+ speed = SPEED_10;
+ break;
+ case 1:
+ speed = SPEED_100;
+ break;
+ case 2:
+ speed = SPEED_1000;
+ break;
+ case 4:
+ speed = SPEED_2500;
+ break;
+ default:
+ speed = SPEED_UNKNOWN;
+ break;
+ }
+
+ phydev->speed = speed;
+ phydev->duplex = duplex;
+
+ return 0;
+}
+
+static int yt8821_read_status(struct phy_device *phydev)
+{
+ int ret;
+ int val;
+ int yt8521_fiber_latch_val;
+ int yt8521_fiber_curr_val;
+ int link;
+ int link_utp = 0, link_fiber = 0;
+
+ if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) {
+ /* reading UTP */
+ ret = ytphy_write_ext(phydev, 0xa000, 0);
+ if (ret < 0)
+ return ret;
+
+ val = phy_read(phydev, REG_PHY_SPEC_STATUS);
+ if (val < 0)
+ return val;
+
+ link = val & (BIT(YT8521_LINK_STATUS_BIT));
+ if (link) {
+ if (link_utp == 0)
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, link up, UTP,"
+ "reg 0x11 = 0x%x\n",
+ __func__, phydev->mdio.addr,
+ (unsigned int)val);
+ link_utp = 1;
+ /* speed(2500), duplex */
+ yt8821_adjust_status(phydev, val, 1);
+ } else {
+ link_utp = 0;
+ }
+ } //(YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER)
+
+ if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_UTP) {
+ /* reading Fiber */
+ ret = ytphy_write_ext(phydev, 0xa000, 2);
+ if (ret < 0)
+ return ret;
+
+ val = phy_read(phydev, REG_PHY_SPEC_STATUS);
+ if (val < 0)
+ return val;
+
+ //note: below debug information is used to check multiple PHy ports.
+
+ /* for fiber, from 1000m to 100m, there is not link down from 0x11,
+ * and check reg 1 to identify such case this is important for Linux
+ * kernel for that, missing linkdown event will cause problem.
+ */
+ yt8521_fiber_latch_val = phy_read(phydev, MII_BMSR);
+ yt8521_fiber_curr_val = phy_read(phydev, MII_BMSR);
+ link = val & (BIT(YT8521_LINK_STATUS_BIT));
+ if (link && yt8521_fiber_latch_val != yt8521_fiber_curr_val) {
+ link = 0;
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, fiber link down detect,"
+ "latch = %04x, curr = %04x\n",
+ __func__, phydev->mdio.addr,
+ yt8521_fiber_latch_val,
+ yt8521_fiber_curr_val);
+ }
+
+ if (link) {
+ if (link_fiber == 0)
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, link up, Fiber,"
+ "reg 0x11 = 0x%x\n",
+ __func__, phydev->mdio.addr,
+ (unsigned int)val);
+ link_fiber = 1;
+ yt8821_adjust_status(phydev, val, 0);
+ } else {
+ link_fiber = 0;
+ }
+ } //(YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_UTP)
+
+ if (link_utp || link_fiber) {
+ if (phydev->link == 0)
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, link up, media %s,"
+ "reg 0x11 = 0x%x\n",
+ __func__, phydev->mdio.addr,
+ (link_utp && link_fiber) ?
+ "both UTP and Fiber" :
+ (link_utp ? "UTP" : "Fiber"),
+ (unsigned int)val);
+ phydev->link = 1;
+ } else {
+ if (phydev->link == 1)
+ netdev_info(phydev->attached_dev,
+ "%s, phy addr: %d, link down\n",
+ __func__, phydev->mdio.addr);
+ phydev->link = 0;
+ }
+
+ if (YT8521_PHY_MODE_CURR != YT8521_PHY_MODE_FIBER) {
+ if (link_utp)
+ ytphy_write_ext(phydev, 0xa000, 0);
+ }
+ return 0;
+}
+
+static int yt8821_get_features(struct phy_device *phydev)
+{
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported, 1);
+ return genphy_read_abilities(phydev);
+}
+
+static struct phy_driver ytphy_drvs[] = {
{
- PHY_ID_MATCH_EXACT(PHY_ID_YT8521),
- .name = "YT8521 Gigabit Ethernet",
- .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
- .flags = PHY_POLL,
- .soft_reset = yt8521_soft_reset,
- .config_aneg = genphy_config_aneg,
- .aneg_done = genphy_aneg_done,
- .config_init = yt8521_config_init,
- .read_status = yt8521_read_status,
- .suspend = yt8521_suspend,
- .resume = yt8521_resume,
-#if (YTPHY_ENABLE_WOL)
- .get_wol = &ytphy_get_wol,
- .set_wol = &ytphy_set_wol,
+ .phy_id = PHY_ID_YT8010,
+ .name = "YT8010 Automotive Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_POLL,
+ .soft_reset = yt8010_soft_reset,
+ .config_aneg = yt8010_config_aneg,
+ .aneg_done = yt8010_aneg_done,
+ .config_init = ytphy_config_init,
+ .read_status = yt8010_read_status,
+ }, {
+ .phy_id = PHY_ID_YT8010AS,
+ .name = "YT8010AS Automotive Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_POLL,
+ .soft_reset = yt8010AS_soft_reset,
+ .aneg_done = yt8010_aneg_done,
+ .config_init = yt8010AS_config_init,
+ .read_status = yt8010_read_status,
+ }, {
+ .phy_id = PHY_ID_YT8510,
+ .name = "YT8510 100/10Mb Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_POLL,
+ .config_aneg = genphy_config_aneg,
+ .config_init = ytphy_config_init,
+ .read_status = genphy_read_status,
+ }, {
+ .phy_id = PHY_ID_YT8511,
+ .name = "YT8511 Gigabit Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_POLL,
+ .config_aneg = genphy_config_aneg,
+#if GMAC_CLOCK_INPUT_NEEDED
+ .config_init = yt8511_config_init,
+#else
+ .config_init = ytphy_config_init,
#endif
+ .read_status = genphy_read_status,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ }, {
+ .phy_id = PHY_ID_YT8512,
+ .name = "YT8512 Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_POLL,
+ .config_aneg = genphy_config_aneg,
+ .config_init = yt8512_config_init,
+ .read_status = yt8512_read_status,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ }, {
+ .phy_id = PHY_ID_YT8512B,
+ .name = "YT8512B Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_POLL,
+ .config_aneg = genphy_config_aneg,
+ .config_init = yt8512_config_init,
+ .read_status = yt8512_read_status,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ }, {
+ .phy_id = PHY_ID_YT8521,
+ .name = "YT8521 Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_POLL,
+ .soft_reset = yt8521_soft_reset,
+ .config_aneg = genphy_config_aneg,
+ .aneg_done = yt8521_aneg_done,
+ .config_init = yt8521_config_init,
+ .read_status = yt8521_read_status,
+ .suspend = yt8521_suspend,
+ .resume = yt8521_resume,
+#if (YTPHY_WOL_FEATURE_ENABLE)
+ .get_wol = &ytphy_wol_feature_get,
+ .set_wol = &ytphy_wol_feature_set,
+#endif
+ }, {
+ /* same as 8521 */
+ .phy_id = PHY_ID_YT8531S,
+ .name = "YT8531S Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_POLL,
+ .soft_reset = yt8521_soft_reset,
+ .config_aneg = genphy_config_aneg,
+ .aneg_done = yt8521_aneg_done,
+ .config_init = yt8531S_config_init,
+ .read_status = yt8521_read_status,
+ .suspend = yt8521_suspend,
+ .resume = yt8521_resume,
+#if (YTPHY_WOL_FEATURE_ENABLE)
+ .get_wol = &ytphy_wol_feature_get,
+ .set_wol = &ytphy_wol_feature_set,
+#endif
+ }, {
+ /* same as 8511 */
+ .phy_id = PHY_ID_YT8531,
+ .name = "YT8531 Gigabit Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_POLL,
+ .config_aneg = genphy_config_aneg,
+
+ .config_init = yt8531_config_init,
+ .read_status = genphy_read_status,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+#if (YTPHY_WOL_FEATURE_ENABLE)
+ .get_wol = &ytphy_wol_feature_get,
+ .set_wol = &ytphy_wol_feature_set,
+#endif
+ }, {
+ .phy_id = PHY_ID_YT8618,
+ .name = "YT8618 Ethernet",
+ .phy_id_mask = MOTORCOMM_MPHY_ID_MASK,
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_POLL,
+ .soft_reset = yt8618_soft_reset,
+ .config_aneg = genphy_config_aneg,
+ .aneg_done = yt8618_aneg_done,
+ .config_init = yt8618_config_init,
+ .read_status = yt8618_read_status,
+ .suspend = yt8618_suspend,
+ .resume = yt8618_resume,
+ },
+ {
+ .phy_id = PHY_ID_YT8614,
+ .name = "YT8614 Ethernet",
+ .phy_id_mask = MOTORCOMM_MPHY_ID_MASK_8614,
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_POLL,
+ .soft_reset = yt8614_soft_reset,
+ .config_aneg = genphy_config_aneg,
+ .aneg_done = yt8614_aneg_done,
+ .config_init = yt8614_config_init,
+ .read_status = yt8614_read_status,
+ .suspend = yt8614_suspend,
+ .resume = yt8614_resume,
+ },
+ {
+ .phy_id = PHY_ID_YT8821,
+ .name = "YT8821 2.5Gb Ethernet",
+ .phy_id_mask = MOTORCOMM_PHY_ID_MASK_8821,
+ .flags = PHY_POLL,
+ .soft_reset = yt8821_soft_reset,
+ .config_aneg = genphy_config_aneg,
+ .aneg_done = yt8521_aneg_done,
+ .get_features = yt8821_get_features,
+ .config_init = yt8821_config_init,
+ .read_status = yt8821_read_status,
+ .suspend = yt8521_suspend,
+ .resume = yt8521_resume,
},
};
-module_phy_driver(motorcomm_phy_drvs);
+/* for linux 4.x */
+module_phy_driver(ytphy_drvs);
MODULE_DESCRIPTION("Motorcomm PHY driver");
-MODULE_AUTHOR("Peter Geis");
-MODULE_AUTHOR("Walker Chen <walker.chen@starfivetech.com>");
+MODULE_AUTHOR("Leilei Zhao");
MODULE_LICENSE("GPL");
-static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
- { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
- { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
- { /* sentinal */ }
+static struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
+ { PHY_ID_YT8010, MOTORCOMM_PHY_ID_MASK },
+ { PHY_ID_YT8510, MOTORCOMM_PHY_ID_MASK },
+ { PHY_ID_YT8511, MOTORCOMM_PHY_ID_MASK },
+ { PHY_ID_YT8512, MOTORCOMM_PHY_ID_MASK },
+ { PHY_ID_YT8512B, MOTORCOMM_PHY_ID_MASK },
+ { PHY_ID_YT8521, MOTORCOMM_PHY_ID_MASK },
+ { PHY_ID_YT8531S, MOTORCOMM_PHY_ID_8531_MASK },
+ { PHY_ID_YT8531, MOTORCOMM_PHY_ID_8531_MASK },
+ { PHY_ID_YT8618, MOTORCOMM_MPHY_ID_MASK },
+ { PHY_ID_YT8614, MOTORCOMM_MPHY_ID_MASK_8614 },
+ { PHY_ID_YT8821, MOTORCOMM_PHY_ID_MASK_8821 },
+ { }
};
MODULE_DEVICE_TABLE(mdio, motorcomm_tbl);
+
embedded controller found in certain e-book readers designed by the
original design manufacturer Netronix.
+config RTC_DRV_STARFIVE
+ tristate "StarFive 32.768k-RTC"
+ depends on SOC_STARFIVE
+ depends on OF
+ help
+ If you say Y here you will get support for the RTC found on
+ StarFive SOCS.
+
comment "on-CPU RTC drivers"
config RTC_DRV_ASM9260
If you say Y here you will get support for the RTC found on
Starfire systems.
-config RTC_DRV_STARFIVE
- tristate "StarFive 32.768k-RTC"
- depends on RISCV
- depends on OF
- help
- If you say Y here you will get support for the RTC found on
- StarFive SOCS.
-
config RTC_DRV_MV
tristate "Marvell SoC RTC"
depends on ARCH_DOVE || ARCH_MVEBU || COMPILE_TEST
regcache_cache_bypass(map, true);
r = regmap_read(map, i, &v);
if (r) {
- dev_err(dev, "failed to read register %d\n", i);
+ dev_dbg(dev, "failed to read register %d\n", i);
continue;
}
regcache_cache_bypass(map, false);
#define _MASTER_INDEX 0
/**
- * TODO:
- * 1, add PM API: ac108_suspend,ac108_resume
- * 2,0x65-0x6a
- * 3,0x76-0x79 high 4bit
+ * TODO:
+ * 1, add PM API: ac108_suspend,ac108_resume
+ * 2,0x65-0x6a
+ * 3,0x76-0x79 high 4bit
*/
struct pll_div {
unsigned int freq_in;
{ 32, 7 },
};
-static const unsigned ac108_bclkdivs[] = {
+static const unsigned int ac108_bclkdivs[] = {
0, 1, 2, 4,
6, 8, 12, 16,
24, 32, 48, 64,
/* FOUT =(FIN * N) / [(M1+1) * (M2+1)*(K1+1)*(K2+1)] ; M1[0,31], M2[0,1], N[0,1023], K1[0,31], K2[0,1] */
static const struct pll_div ac108_pll_div_list[] = {
{ 400000, _FREQ_24_576K, 0, 0, 614, 4, 1 },
- { 512000, _FREQ_24_576K, 0, 0, 960, 9, 1 }, //_FREQ_24_576K/48
- { 768000, _FREQ_24_576K, 0, 0, 640, 9, 1 }, //_FREQ_24_576K/32
+ { 512000, _FREQ_24_576K, 0, 0, 960, 9, 1 }, /* _FREQ_24_576K/48 */
+ { 768000, _FREQ_24_576K, 0, 0, 640, 9, 1 }, /* _FREQ_24_576K/32 */
{ 800000, _FREQ_24_576K, 0, 0, 614, 9, 1 },
- { 1024000, _FREQ_24_576K, 0, 0, 480, 9, 1 }, //_FREQ_24_576K/24
+ { 1024000, _FREQ_24_576K, 0, 0, 480, 9, 1 }, /* _FREQ_24_576K/24 */
{ 1600000, _FREQ_24_576K, 0, 0, 307, 9, 1 },
{ 2048000, _FREQ_24_576K, 0, 0, 240, 9, 1 }, /* accurate, 8000 * 256 */
{ 3072000, _FREQ_24_576K, 0, 0, 160, 9, 1 }, /* accurate, 12000 * 256 */
{ 16000000, _FREQ_24_576K, 12, 0, 400, 9, 1 },
{ 19200000, _FREQ_24_576K, 15, 0, 410, 9, 1 },
{ 19680000, _FREQ_24_576K, 15, 0, 400, 9, 1 },
- { 24000000, _FREQ_24_576K, 4, 0, 128,24, 0 }, // accurate, 24M -> 24.576M */
+ { 24000000, _FREQ_24_576K, 9, 0, 256,24, 0 }, /* accurate, 24M -> 24.576M */
{ 400000, _FREQ_22_579K, 0, 0, 566, 4, 1 },
{ 512000, _FREQ_22_579K, 0, 0, 880, 9, 1 },
{ 16000000, _FREQ_22_579K, 11, 0, 340, 9, 1 },
{ 19200000, _FREQ_22_579K, 13, 0, 330, 9, 1 },
{ 19680000, _FREQ_22_579K, 14, 0, 345, 9, 1 },
- { 24000000, _FREQ_22_579K, 24, 0, 588,24, 0 }, // accurate, 24M -> 22.5792M */
-
-
- { _FREQ_24_576K / 1, _FREQ_24_576K, 9, 0, 200, 9, 1 }, //_FREQ_24_576K
- { _FREQ_24_576K / 2, _FREQ_24_576K, 9, 0, 400, 9, 1 }, /*12288000,accurate, 48000 * 256 */
- { _FREQ_24_576K / 4, _FREQ_24_576K, 4, 0, 400, 9, 1 }, /*6144000, accurate, 24000 * 256 */
- { _FREQ_24_576K / 16, _FREQ_24_576K, 0, 0, 320, 9, 1 }, //1536000
- { _FREQ_24_576K / 64, _FREQ_24_576K, 0, 0, 640, 4, 1 }, //384000
- { _FREQ_24_576K / 96, _FREQ_24_576K, 0, 0, 960, 4, 1 }, //256000
- { _FREQ_24_576K / 128, _FREQ_24_576K, 0, 0, 512, 1, 1 }, //192000
- { _FREQ_24_576K / 176, _FREQ_24_576K, 0, 0, 880, 4, 0 }, //140000
- { _FREQ_24_576K / 192, _FREQ_24_576K, 0, 0, 960, 4, 0 }, //128000
-
- { _FREQ_22_579K / 1, _FREQ_22_579K, 9, 0, 200, 9, 1 }, //_FREQ_22_579K
- { _FREQ_22_579K / 2, _FREQ_22_579K, 9, 0, 400, 9, 1 }, /*11289600,accurate, 44100 * 256 */
- { _FREQ_22_579K / 4, _FREQ_22_579K, 4, 0, 400, 9, 1 }, /*5644800, accurate, 22050 * 256 */
- { _FREQ_22_579K / 16, _FREQ_22_579K, 0, 0, 320, 9, 1 }, //1411200
- { _FREQ_22_579K / 64, _FREQ_22_579K, 0, 0, 640, 4, 1 }, //352800
- { _FREQ_22_579K / 96, _FREQ_22_579K, 0, 0, 960, 4, 1 }, //235200
- { _FREQ_22_579K / 128, _FREQ_22_579K, 0, 0, 512, 1, 1 }, //176400
- { _FREQ_22_579K / 176, _FREQ_22_579K, 0, 0, 880, 4, 0 }, //128290
- { _FREQ_22_579K / 192, _FREQ_22_579K, 0, 0, 960, 4, 0 }, //117600
-
- { _FREQ_22_579K / 6, _FREQ_22_579K, 2, 0, 360, 9, 1 }, //3763200
- { _FREQ_22_579K / 8, _FREQ_22_579K, 0, 0, 160, 9, 1 }, /*2822400, accurate, 11025 * 256 */
- { _FREQ_22_579K / 12, _FREQ_22_579K, 0, 0, 240, 9, 1 }, //1881600
- { _FREQ_22_579K / 24, _FREQ_22_579K, 0, 0, 480, 9, 1 }, //940800
- { _FREQ_22_579K / 32, _FREQ_22_579K, 0, 0, 640, 9, 1 }, //705600
- { _FREQ_22_579K / 48, _FREQ_22_579K, 0, 0, 960, 9, 1 }, //470400
+ { 24000000, _FREQ_22_579K, 24, 0, 588,24, 0 }, /* accurate, 24M -> 22.5792M */
+
+
+ { _FREQ_24_576K / 1, _FREQ_24_576K, 9, 0, 200, 9, 1 }, /* _FREQ_24_576K */
+ { _FREQ_24_576K / 2, _FREQ_24_576K, 9, 0, 400, 9, 1 }, /* 12288000,accurate, 48000 * 256 */
+ { _FREQ_24_576K / 4, _FREQ_24_576K, 4, 0, 400, 9, 1 }, /* 6144000, accurate, 24000 * 256 */
+ { _FREQ_24_576K / 16, _FREQ_24_576K, 0, 0, 320, 9, 1 }, /* 1536000 */
+ { _FREQ_24_576K / 64, _FREQ_24_576K, 0, 0, 640, 4, 1 }, /* 384000 */
+ { _FREQ_24_576K / 96, _FREQ_24_576K, 0, 0, 960, 4, 1 }, /* 256000 */
+ { _FREQ_24_576K / 128, _FREQ_24_576K, 0, 0, 512, 1, 1 }, /* 192000 */
+ { _FREQ_24_576K / 176, _FREQ_24_576K, 0, 0, 880, 4, 0 }, /* 140000 */
+ { _FREQ_24_576K / 192, _FREQ_24_576K, 0, 0, 960, 4, 0 }, /* 128000 */
+
+ { _FREQ_22_579K / 1, _FREQ_22_579K, 9, 0, 200, 9, 1 }, /* _FREQ_22_579K */
+ { _FREQ_22_579K / 2, _FREQ_22_579K, 9, 0, 400, 9, 1 }, /* 11289600,accurate, 44100 * 256 */
+ { _FREQ_22_579K / 4, _FREQ_22_579K, 4, 0, 400, 9, 1 }, /* 5644800, accurate, 22050 * 256 */
+ { _FREQ_22_579K / 16, _FREQ_22_579K, 0, 0, 320, 9, 1 }, /* 1411200 */
+ { _FREQ_22_579K / 64, _FREQ_22_579K, 0, 0, 640, 4, 1 }, /* 352800 */
+ { _FREQ_22_579K / 96, _FREQ_22_579K, 0, 0, 960, 4, 1 }, /* 235200 */
+ { _FREQ_22_579K / 128, _FREQ_22_579K, 0, 0, 512, 1, 1 }, /* 176400 */
+ { _FREQ_22_579K / 176, _FREQ_22_579K, 0, 0, 880, 4, 0 }, /* 128290 */
+ { _FREQ_22_579K / 192, _FREQ_22_579K, 0, 0, 960, 4, 0 }, /* 117600 */
+
+ { _FREQ_22_579K / 6, _FREQ_22_579K, 2, 0, 360, 9, 1 }, /* 3763200 */
+ { _FREQ_22_579K / 8, _FREQ_22_579K, 0, 0, 160, 9, 1 }, /* 2822400, accurate, 11025 * 256 */
+ { _FREQ_22_579K / 12, _FREQ_22_579K, 0, 0, 240, 9, 1 }, /* 1881600 */
+ { _FREQ_22_579K / 24, _FREQ_22_579K, 0, 0, 480, 9, 1 }, /* 940800 */
+ { _FREQ_22_579K / 32, _FREQ_22_579K, 0, 0, 640, 9, 1 }, /* 705600 */
+ { _FREQ_22_579K / 48, _FREQ_22_579K, 0, 0, 960, 9, 1 }, /* 470400 */
};
SNDRV_PCM_FMTBIT_S32_LE)
static const DECLARE_TLV_DB_SCALE(tlv_adc_pga_gain, 0, 100, 0);
-static const DECLARE_TLV_DB_SCALE(tlv_ch_digital_vol, -11925,75,0);
+static const DECLARE_TLV_DB_SCALE(tlv_ch_digital_vol, -11925, 75, 0);
-int ac10x_read(u8 reg, u8* rt_val, struct regmap* i2cm) {
+int ac10x_read(u8 reg, u8* rt_val, struct regmap* i2cm)
+{
int r, v = 0;
- if ((r = regmap_read(i2cm, reg, &v)) < 0) {
- pr_err("ac10x_read error->[REG-0x%02x]\n", reg);
- } else {
+ if ((r = regmap_read(i2cm, reg, &v)) < 0)
+ pr_info("ac10x_read info->[REG-0x%02x]\n", reg);
+ else
*rt_val = v;
- }
return r;
}
-int ac10x_write(u8 reg, u8 val, struct regmap* i2cm) {
+int ac10x_write(u8 reg, u8 val, struct regmap* i2cm)
+{
int r;
- if ((r = regmap_write(i2cm, reg, val)) < 0) {
- pr_err("ac10x_write error->[REG-0x%02x,val-0x%02x]\n", reg, val);
- }
+ if ((r = regmap_write(i2cm, reg, val)) < 0)
+ pr_info("ac10x_write info->[REG-0x%02x,val-0x%02x]\n", reg, val);
return r;
}
-int ac10x_update_bits(u8 reg, u8 mask, u8 val, struct regmap* i2cm) {
+int ac10x_update_bits(u8 reg, u8 mask, u8 val, struct regmap* i2cm)
+{
int r;
- if ((r = regmap_update_bits(i2cm, reg, mask, val)) < 0) {
- pr_err("%s() error->[REG-0x%02x,val-0x%02x]\n", __func__, reg, val);
- }
+ if ((r = regmap_update_bits(i2cm, reg, mask, val)) < 0)
+ pr_info("%s() info->[REG-0x%02x,val-0x%02x]\n", __func__, reg, val);
return r;
}
* Returns 0 for success.
*/
static int snd_ac108_get_volsw(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol
-){
+ struct snd_ctl_elem_value *ucontrol)
+{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int mask = (1 << fls(mc->max)) - 1;
* Returns 0 for success.
*/
static int snd_ac108_put_volsw(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol
-){
+ struct snd_ctl_elem_value *ucontrol)
+{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int sign_bit = mc->sign_bit;
static const struct snd_soc_dapm_widget ac108_dapm_widgets[] = {
- //input widgets
+ /* input widgets */
SND_SOC_DAPM_INPUT("MIC1P"),
SND_SOC_DAPM_INPUT("MIC1N"),
};
-static int ac108_multi_write(u8 reg, u8 val, struct ac10x_priv *ac10x) {
+static int ac108_multi_write(u8 reg, u8 val, struct ac10x_priv *ac10x)
+{
u8 i;
- for (i = 0; i < ac10x->codec_cnt; i++) {
+ for (i = 0; i < ac10x->codec_cnt; i++)
ac10x_write(reg, val, ac10x->i2cmap[i]);
- }
return 0;
}
-static int ac108_multi_update_bits(u8 reg, u8 mask, u8 val, struct ac10x_priv *ac10x) {
+static int ac108_multi_update_bits(u8 reg, u8 mask, u8 val, struct ac10x_priv *ac10x)
+{
int r = 0;
u8 i;
-
- for (i = 0; i < ac10x->codec_cnt; i++) {
+ for (i = 0; i < ac10x->codec_cnt; i++)
r |= ac10x_update_bits(reg, mask, val, ac10x->i2cmap[i]);
- }
return r;
}
-static unsigned int ac108_codec_read(struct snd_soc_codec *codec, unsigned int reg) {
+static unsigned int ac108_codec_read(struct snd_soc_codec *codec, unsigned int reg)
+{
unsigned char val_r;
struct ac10x_priv *ac10x = dev_get_drvdata(codec->dev);
/*read one chip is fine*/
return val_r;
}
-static int ac108_codec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) {
+static int ac108_codec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val)
+{
struct ac10x_priv *ac10x = dev_get_drvdata(codec->dev);
ac108_multi_write(reg, val, ac10x);
return 0;
* The Power management related registers are Reg01h~Reg09h
* 0x01-0x05,0x08,use the default value
* @author baozhu (17-6-21)
- *
- * @param ac10x
+ *
+ * @param ac10x
*/
-static void ac108_configure_power(struct ac10x_priv *ac10x) {
+static void ac108_configure_power(struct ac10x_priv *ac10x)
+{
/**
* 0x06:Enable Analog LDO
*/
ac108_multi_update_bits(PWR_CTRL6, 0x01 << LDO33ANA_ENABLE, 0x01 << LDO33ANA_ENABLE, ac10x);
/**
* 0x07:
- * Control VREF output and micbias voltage ?
- * REF faststart disable, enable Enable VREF (needed for Analog
- * LDO and MICBIAS)
+ * Control VREF output and micbias voltage ?
+ * REF faststart disable, enable Enable VREF (needed for Analog
+ * LDO and MICBIAS)
*/
ac108_multi_update_bits(PWR_CTRL7, 0x1f << VREF_SEL | 0x01 << VREF_FASTSTART_ENABLE | 0x01 << VREF_ENABLE,
0x13 << VREF_SEL | 0x00 << VREF_FASTSTART_ENABLE | 0x01 << VREF_ENABLE, ac10x);
/**
- * 0x09:
- * Disable fast-start circuit on VREFP
- * VREFP_RESCTRL=00=1 MOhm
- * IGEN_TRIM=100=+25%
- * Enable VREFP (needed by all audio input channels)
+ * 0x09:
+ * Disable fast-start circuit on VREFP
+ * VREFP_RESCTRL=00=1 MOhm
+ * IGEN_TRIM=100=+25%
+ * Enable VREFP (needed by all audio input channels)
*/
ac108_multi_update_bits(PWR_CTRL9, 0x01 << VREFP_FASTSTART_ENABLE | 0x03 << VREFP_RESCTRL | 0x07 << IGEN_TRIM | 0x01 << VREFP_ENABLE,
0x00 << VREFP_FASTSTART_ENABLE | 0x00 << VREFP_RESCTRL | 0x04 << IGEN_TRIM | 0x01 << VREFP_ENABLE,
* The clock management related registers are Reg20h~Reg25h
* The PLL management related registers are Reg10h~Reg18h.
* @author baozhu (17-6-20)
- *
- * @param ac10x
+ *
+ * @param ac10x
* @param rate : sample rate
- *
+ *
* @return int : fail or success
*/
-static int ac108_config_pll(struct ac10x_priv *ac10x, unsigned rate, unsigned lrck_ratio) {
+static int ac108_config_pll(struct ac10x_priv *ac10x, unsigned rate, unsigned lrck_ratio)
+{
unsigned int i = 0;
struct pll_div ac108_pll_div = { 0 };
for (i = 0; i < ARRAY_SIZE(ac108_pll_div_list); i++) {
if (ac108_pll_div_list[i].freq_in == pll_freq_in && ac108_pll_div_list[i].freq_out % rate == 0) {
ac108_pll_div = ac108_pll_div_list[i];
- dev_dbg(&ac10x->i2c[_MASTER_INDEX]->dev, "AC108 PLL freq_in match:%u, freq_out:%u\n\n",
+ dev_info(&ac10x->i2c[_MASTER_INDEX]->dev, "AC108 PLL freq_in match:%u, freq_out:%u\n\n",
ac108_pll_div.freq_in, ac108_pll_div.freq_out);
break;
}
* 0x20: enable pll, pll source from mclk/bclk, sysclk source from pll, enable sysclk
*/
ac108_multi_update_bits(SYSCLK_CTRL, 0x01 << PLLCLK_EN | 0x03 << PLLCLK_SRC | 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN,
- 0x01 << PLLCLK_EN |pll_src<< PLLCLK_SRC | 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN, ac10x);
+ 0x01 << PLLCLK_EN | pll_src << PLLCLK_SRC | 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN, ac10x);
ac10x->mclk = ac108_pll_div.freq_out;
}
if (ac10x->clk_id == SYSCLK_SRC_MCLK) {
/*
* support no more than 16 slots.
*/
-static int ac108_multi_chips_slots(struct ac10x_priv *ac, int slots) {
+static int ac108_multi_chips_slots(struct ac10x_priv *ac, int slots)
+{
int i;
/*
return 0;
}
-static int ac108_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) {
+static int ac108_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
unsigned int i, channels, samp_res, rate;
struct snd_soc_codec *codec = dai->codec;
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
samp_res = 6;
break;
default:
- pr_err("AC108 don't supported the sample resolution: %u\n", params_format(params));
+ dev_err(dai->dev, "AC108 don't supported the sample resolution: %u\n", params_format(params));
return -EINVAL;
}
return 0;
}
-static int ac108_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) {
+static int ac108_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir)
+{
struct ac10x_priv *ac10x = snd_soc_dai_get_drvdata(dai);
freq = 24000000;
clk_id = SYSCLK_SRC_PLL;
- pr_info("%s :%d\n", __FUNCTION__, freq);
-
switch (clk_id) {
case SYSCLK_SRC_MCLK:
ac108_multi_update_bits(SYSCLK_CTRL, 0x1 << SYSCLK_SRC, SYSCLK_SRC_MCLK << SYSCLK_SRC, ac10x);
*
* @return int
*/
-static int ac108_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) {
+static int ac108_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
unsigned char tx_offset, lrck_polarity, brck_polarity;
struct ac10x_priv *ac10x = dev_get_drvdata(dai->dev);
- dev_dbg(dai->dev, "%s\n", __FUNCTION__);
-
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM: /*AC108 Master*/
+ case SND_SOC_DAIFMT_CBM_CFM: /* AC108 Master */
if (! ac10x->i2c101 || _MASTER_MULTI_CODEC == _MASTER_AC108) {
- dev_dbg(dai->dev, "AC108 set to work as Master\n");
/**
* 0x30:chip is master mode ,BCLK & LRCK output
*/
ac108_multi_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN | 0x03 << SDO1_EN | 0x1 << TXEN | 0x1 << GEN,
- 0x00 << LRCK_IOEN | 0x03 << SDO1_EN | 0x1 << TXEN | 0x1 << GEN, ac10x);
+ 0x03 << LRCK_IOEN | 0x01 << SDO1_EN | 0x1 << TXEN | 0x1 << GEN, ac10x);
/* multi_chips: only one chip set as Master, and the others also need to set as Slave */
ac10x_update_bits(I2S_CTRL, 0x3 << LRCK_IOEN, 0x01 << BCLK_IOEN, ac10x->i2cmap[_MASTER_INDEX]);
- break;
} else {
/* TODO: Both cpu_dai and codec_dai(AC108) be set as slave in DTS */
- dev_dbg(dai->dev, "used as slave when AC101 is master\n");
+ dev_err(dai->dev, "used as slave when AC101 is master\n");
}
- case SND_SOC_DAIFMT_CBS_CFS: /*AC108 Slave*/
- dev_dbg(dai->dev, "AC108 set to work as Slave\n");
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS: /* AC108 Slave */
/**
* 0x30:chip is slave mode, BCLK & LRCK input,enable SDO1_EN and
* SDO2_EN, Transmitter Block Enable, Globe Enable
0x00 << LRCK_IOEN | 0x03 << SDO1_EN | 0x0 << TXEN | 0x0 << GEN, ac10x);
break;
default:
- pr_err("AC108 Master/Slave mode config error:%u\n\n", (fmt & SND_SOC_DAIFMT_MASTER_MASK) >> 12);
+ dev_err(dai->dev, "AC108 Master/Slave mode config error:%u\n\n", (fmt & SND_SOC_DAIFMT_MASTER_MASK) >> 12);
return -EINVAL;
}
/*AC108 config I2S/LJ/RJ/PCM format*/
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- dev_dbg(dai->dev, "AC108 config I2S format\n");
ac10x->i2s_mode = LEFT_JUSTIFIED_FORMAT;
tx_offset = 1;
break;
case SND_SOC_DAIFMT_RIGHT_J:
- dev_dbg(dai->dev, "AC108 config RIGHT-JUSTIFIED format\n");
ac10x->i2s_mode = RIGHT_JUSTIFIED_FORMAT;
tx_offset = 0;
break;
case SND_SOC_DAIFMT_LEFT_J:
- dev_dbg(dai->dev, "AC108 config LEFT-JUSTIFIED format\n");
ac10x->i2s_mode = LEFT_JUSTIFIED_FORMAT;
tx_offset = 0;
break;
case SND_SOC_DAIFMT_DSP_A:
- dev_dbg(dai->dev, "AC108 config PCM-A format\n");
ac10x->i2s_mode = PCM_FORMAT;
tx_offset = 1;
break;
case SND_SOC_DAIFMT_DSP_B:
- dev_dbg(dai->dev, "AC108 config PCM-B format\n");
ac10x->i2s_mode = PCM_FORMAT;
tx_offset = 0;
break;
default:
- pr_err("AC108 I2S format config error:%u\n\n", fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ dev_err(dai->dev, "AC108 I2S format config error:%u\n\n", fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL;
}
/*AC108 config BCLK&LRCK polarity*/
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
- dev_dbg(dai->dev, "AC108 config BCLK&LRCK polarity: BCLK_normal,LRCK_normal\n");
brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P;
lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW;
break;
case SND_SOC_DAIFMT_NB_IF:
- dev_dbg(dai->dev, "AC108 config BCLK&LRCK polarity: BCLK_normal,LRCK_invert\n");
brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P;
lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH;
break;
case SND_SOC_DAIFMT_IB_NF:
- dev_dbg(dai->dev, "AC108 config BCLK&LRCK polarity: BCLK_invert,LRCK_normal\n");
brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N;
lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW;
break;
case SND_SOC_DAIFMT_IB_IF:
- dev_dbg(dai->dev, "AC108 config BCLK&LRCK polarity: BCLK_invert,LRCK_invert\n");
brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N;
lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH;
break;
default:
- pr_err("AC108 config BCLK/LRCLK polarity error:%u\n\n", (fmt & SND_SOC_DAIFMT_INV_MASK) >> 8);
+ dev_err(dai->dev, "AC108 config BCLK/LRCLK polarity error:%u\n\n", (fmt & SND_SOC_DAIFMT_INV_MASK) >> 8);
return -EINVAL;
}
/*
* due to miss channels order in cpu_dai, we meed defer the clock starting.
*/
-static int ac108_set_clock(int y_start_n_stop) {
+static int ac108_set_clock(int y_start_n_stop)
+{
u8 reg;
int ret = 0;
return 0;
}
-static int ac108_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_codec *codec = dai->codec;
- struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
- unsigned long flags;
- int ret = 0;
- u8 r;
-
- dev_dbg(dai->dev, "%s() stream=%s cmd=%d\n",
- __FUNCTION__,
- snd_pcm_stream_str(substream),
- cmd);
-
- spin_lock_irqsave(&ac10x->lock, flags);
-
- if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) {
- ac101_trigger(substream, cmd, dai);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- goto __ret;
- }
- }
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- /* disable global clock if lrck disabled */
- ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]);
- if ((r & (0x01 << BCLK_IOEN)) && (r & (0x01 << LRCK_IOEN)) == 0) {
- /* disable global clock */
- ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x0 << TXEN | 0x0 << GEN, ac10x);
- }
-
- /* delayed clock starting, move to machine trigger() */
- //msleep(10);
- ac108_set_clock(1);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- //msleep(10);
- ac108_set_clock(0);
- break;
- default:
- ret = -EINVAL;
- }
-
-__ret:
- spin_unlock_irqrestore(&ac10x->lock, flags);
-
- return ret;
-}
-
int ac108_audio_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai
-) {
+ struct snd_soc_dai *dai)
+{
struct snd_soc_codec *codec = dai->codec;
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
}
void ac108_aif_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai
-) {
+ struct snd_soc_dai *dai)
+{
struct snd_soc_codec *codec = dai->codec;
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
}
}
-int ac108_aif_mute(struct snd_soc_dai *dai, int mute, int stream) {
+int ac108_aif_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
struct snd_soc_codec *codec = dai->codec;
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
/*ALSA PCM audio operations*/
.hw_params = ac108_hw_params,
.prepare = ac108_prepare,
- /*.trigger = ac108_trigger, */
.mute_stream = ac108_aif_mute,
/*DAI format configuration*/
.set_fmt = ac108_set_fmt,
-
- // .hw_free = ac108_hw_free,
};
static struct snd_soc_dai_driver ac108_dai0 = {
&ac108_dai1,
};
-static int ac108_add_widgets(struct snd_soc_codec *codec) {
+static int ac108_add_widgets(struct snd_soc_codec *codec)
+{
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
const struct snd_kcontrol_new* snd_kcntl = ac108_snd_controls;
return 0;
}
-static int ac108_codec_probe(struct snd_soc_codec *codec) {
+static int ac108_codec_probe(struct snd_soc_codec *codec)
+{
spin_lock_init(&ac10x->lock);
ac10x->codec = codec;
return 0;
}
-static int ac108_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) {
+static int ac108_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level)
+{
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "AC108 level:%d\n", level);
#define ac108_codec_remove ac108_codec_remove_void
#endif
-int ac108_codec_suspend(struct snd_soc_codec *codec) {
+int ac108_codec_suspend(struct snd_soc_codec *codec)
+{
struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec);
int i;
.write = ac108_codec_write,
};
-static ssize_t ac108_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) {
+static ssize_t ac108_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
int val = 0, flag = 0;
u8 i = 0, reg, num, value_w, value_r[4];
reg = (val >> 8) & 0xFF;
value_w = val & 0xFF;
ac108_multi_write(reg, value_w, ac10x);
- printk("Write 0x%02x to REG:0x%02x\n", value_w, reg);
+ dev_info(dev, "Write 0x%02x to REG:0x%02x\n", value_w, reg);
} else {
int k;
reg = (val >> 8) & 0xFF;
num = val & 0xff;
- printk("\nRead: start REG:0x%02x,count:0x%02x\n", reg, num);
+ dev_info(dev, "\nRead: start REG:0x%02x,count:0x%02x\n", reg, num);
- for (k = 0; k < ac10x->codec_cnt; k++) {
+ for (k = 0; k < ac10x->codec_cnt; k++)
regcache_cache_bypass(ac10x->i2cmap[k], true);
- }
- do {
+ do {
memset(value_r, 0, sizeof value_r);
- for (k = 0; k < ac10x->codec_cnt; k++) {
+ for (k = 0; k < ac10x->codec_cnt; k++)
ac10x_read(reg, &value_r[k], ac10x->i2cmap[k]);
- }
- if (ac10x->codec_cnt >= 2) {
- printk("REG[0x%02x]: 0x%02x 0x%02x", reg, value_r[0], value_r[1]);
- } else {
- printk("REG[0x%02x]: 0x%02x", reg, value_r[0]);
- }
+
+ if (ac10x->codec_cnt >= 2)
+ dev_info(dev, "REG[0x%02x]: 0x%02x 0x%02x", reg, value_r[0], value_r[1]);
+ else
+ dev_info(dev, "REG[0x%02x]: 0x%02x", reg, value_r[0]);
reg++;
- if ((++i == num) || (i % 4 == 0)) {
- printk("\n");
- }
+ if ((++i == num) || (i % 4 == 0))
+ dev_info(dev, "\n");
} while (i < num);
- for (k = 0; k < ac10x->codec_cnt; k++) {
+
+ for (k = 0; k < ac10x->codec_cnt; k++)
regcache_cache_bypass(ac10x->i2cmap[k], false);
- }
}
return count;
}
-static ssize_t ac108_show(struct device *dev, struct device_attribute *attr, char *buf) {
-#if 1
- printk("echo flag|reg|val > ac108\n");
- printk("eg read star addres=0x06,count 0x10:echo 0610 >ac108\n");
- printk("eg write value:0xfe to address:0x06 :echo 106fe > ac108\n");
+static ssize_t ac108_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ dev_info(dev, "echo flag|reg|val > ac108\n");
+ dev_info(dev, "eg read star addres=0x06,count 0x10:echo 0610 >ac108\n");
+ dev_info(dev, "eg write value:0xfe to address:0x06 :echo 106fe > ac108\n");
return 0;
-#else
- return snprintf(buf, PAGE_SIZE,"echo flag|reg|val > ac108\n"
- "eg read star addres=0x06,count 0x10:echo 0610 >ac108\n"
- "eg write value:0xfe to address:0x06 :echo 106fe > ac108\n");
-#endif
}
static DEVICE_ATTR(ac108, 0644, ac108_show, ac108_store);
.max_register = 0xDF,
.cache_type = REGCACHE_FLAT,
};
-static int ac108_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i2c_id) {
+static int ac108_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i2c_id)
+{
struct device_node *np = i2c->dev.of_node;
unsigned int val = 0;
int ret = 0, index;
ret = of_property_read_u32(np, "data-protocol", &val);
if (ret) {
- pr_err("Please set data-protocol.\n");
+ dev_err(&i2c->dev, "Please set data-protocol.\n");
return -EINVAL;
}
ac10x->data_protocol = val;
if (of_property_read_u32(np, "tdm-chips-count", &val)) val = 1;
ac10x->tdm_chips_cnt = val;
- pr_err(" ac10x i2c_id number: %d\n", index);
- pr_err(" ac10x data protocol: %d\n", ac10x->data_protocol);
+ dev_info(&i2c->dev, " ac10x i2c_id number: %d\n", index);
+ dev_info(&i2c->dev, " ac10x data protocol: %d\n", ac10x->data_protocol);
ac10x->i2c[index] = i2c;
ac10x->i2cmap[index] = devm_regmap_init_i2c(i2c, &ac108_regmap);
}
/*
- * Writing this register with 0x12
+ * Writing this register with 0x12
* will resets all register to their default state.
*/
regcache_cache_only(ac10x->i2cmap[index], false);
ac10x_fill_regcache(&i2c->dev, ac10x->i2cmap[index]);
ac10x->codec_cnt++;
- pr_err(" ac10x codec count : %d\n", ac10x->codec_cnt);
+ dev_info(&i2c->dev, " ac10x codec count : %d\n", ac10x->codec_cnt);
ret = sysfs_create_group(&i2c->dev.kobj, &ac108_debug_attr_group);
if (ret) {
- pr_err("failed to create attr group\n");
+ dev_err(&i2c->dev, "failed to create attr group\n");
}
__ret:
/* It's time to bind codec to i2c[_MASTER_INDEX] when all i2c are ready */
if ((ac10x->codec_cnt != 0 && ac10x->tdm_chips_cnt < 2)
|| (ac10x->i2c[0] && ac10x->i2c[1] && ac10x->i2c101)) {
- //seeed_voice_card_register_set_clock(SNDRV_PCM_STREAM_CAPTURE, ac108_set_clock);
/* no playback stream */
if (! ac10x->i2c101) {
memset(&ac108_dai[_MASTER_INDEX]->playback, '\0', sizeof ac108_dai[_MASTER_INDEX]->playback);
return ret;
}
-static int ac108_i2c_remove(struct i2c_client *i2c) {
+static int ac108_i2c_remove(struct i2c_client *i2c)
+{
if (ac10x->codec != NULL) {
snd_soc_unregister_codec(&ac10x->i2c[_MASTER_INDEX]->dev);
ac10x->codec = NULL;
#define CLOCK_BASE 0x13020000UL
-static void saif_set_reg(void __iomem *addr, u32 data, u32 shift, u32 mask)
-{
- u32 tmp;
- tmp = readl(addr);
- tmp &= ~mask;
- tmp |= (data << shift) & mask;
- writel(tmp, addr);
-}
-
static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val)
{
writel(val, io_base + reg);
return -EINVAL;
}
- if (txrx == SNDRV_PCM_STREAM_PLAYBACK)
- {
- //_SWITCH_CLOCK_CLK_MCLK_SOURCE_CLK_MCLK_EXT_;
- saif_set_reg(dev->clk_base + 0x48, 1, 24, 0x1000000);
+ if (txrx == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = clk_prepare_enable(dev->clks_dac_bclk);
+ if (ret) {
+ dev_err(dev->dev, "%s: failed to enable clks_dac_bclk\n", __func__);
+ return ret;
+ }
- //_SWITCH_CLOCK_CLK_U1_I2STX_4CH_BCLK_SOURCE_CLK_I2STX_BCLK_EXT_;
- saif_set_reg(dev->clk_base + 0x2B0, 1, 24, 0x1000000);
+ ret = clk_set_parent(dev->clks_bclk, dev->clks_dac_bclk);
+ if (ret) {
+ dev_err(dev->dev, "Can't set clock source for clks_bclk: %d\n", ret);
+ return ret;
+ }
- //_SWITCH_CLOCK_CLK_U1_I2STX_4CH_LRCK_SOURCE_CLK_I2STX_LRCK_EXT_;
- saif_set_reg(dev->clk_base + 0x2B8, 1, 24, 0x1000000);
- }
- else if (txrx == SNDRV_PCM_STREAM_CAPTURE)
- {
- //_SWITCH_CLOCK_CLK_MCLK_SOURCE_CLK_MCLK_EXT_;
- saif_set_reg(dev->clk_base + 0x48, 1, 24, 0x1000000);
+ ret = clk_prepare_enable(dev->clks_dac_lrck);
+ if (ret) {
+ dev_err(dev->dev, "%s: failed to enable clks_dac_lrck\n", __func__);
+ return ret;
+ }
+
+ ret = clk_set_parent(dev->clks_lrclk, dev->clks_dac_lrck);
+ if (ret) {
+ dev_err(dev->dev, "Can't set clock source for clks_lrclk: %d\n", ret);
+ return ret;
+ }
+ }else if (txrx == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = clk_prepare_enable(dev->clks[CLK_ADC_BCLK_EXT]);
+ if (ret) {
+ dev_err(dev->dev, "%s: failed to enable CLK_ADC_BCLK_EXT\n", __func__);
+ return ret;
+ }
- //_SWITCH_CLOCK_CLK_U1_I2STX_4CH_BCLK_SOURCE_CLK_I2STX_BCLK_EXT_;
- saif_set_reg(dev->clk_base + 0x2CC, 1, 24, 0x1000000);
+ ret = clk_set_parent(dev->clks[CLK_ADC_RX_BCLK], dev->clks[CLK_ADC_BCLK_EXT]);
+ if (ret) {
+ dev_err(dev->dev, "Can't set clock source for CLK_ADC_RX_BCLK: %d\n", ret);
+ return ret;
+ }
- //_SWITCH_CLOCK_CLK_U1_I2STX_4CH_LRCK_SOURCE_CLK_I2STX_LRCK_EXT_;
- saif_set_reg(dev->clk_base + 0x2D4, 1, 24, 0x1000000);
+ ret = clk_prepare_enable(dev->clks[CLK_ADC_LRCK_EXT]);
+ if (ret) {
+ dev_err(dev->dev, "%s: failed to enable CLK_ADC_LRCK_EXT\n", __func__);
+ return ret;
+ }
+
+ ret = clk_set_parent(dev->clks[CLK_ADC_RX_LRCK], dev->clks[CLK_ADC_LRCK_EXT]);
+ if (ret) {
+ dev_err(dev->dev, "Can't set clock source for CLK_ADC_RX_LRCK: %d\n", ret);
+ return ret;
+ }
}
dw_i2s_config(dev, substream->stream);
{ .id = "3ch-lrck" },
{ .id = "rx-bclk" },
{ .id = "rx-lrck" },
+ { .id = "mclk" },
+ { .id = "bclk-ext" },
+ { .id = "lrck-ext" },
};
ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks);
dev->clks[CLK_ADC_LRCLK] = clks[5].clk;
dev->clks[CLK_ADC_RX_BCLK] = clks[6].clk;
dev->clks[CLK_ADC_RX_LRCK] = clks[7].clk;
+ dev->clks[CLK_ADC_MCLK] = clks[8].clk;
+ dev->clks[CLK_ADC_BCLK_EXT] = clks[9].clk;
+ dev->clks[CLK_ADC_LRCK_EXT] = clks[10].clk;
ret = clk_prepare_enable(dev->clks[CLK_ADC_APB0]);
if (ret) {
dev_err(&pdev->dev, "%s: failed to enable CLK_ADC_AUDROOT\n", __func__);
goto disable_audroot_clk;
}
+
ret = clk_set_rate(dev->clks[CLK_ADC_AUDROOT], 204800000);
if (ret) {
dev_err(&pdev->dev, "failed to set rate for CLK_ADC_MCLK \n");
if (IS_ERR(dev->clks_4ch_apb))
return PTR_ERR(dev->clks_4ch_apb);
+ dev->clks_dac_bclk = devm_clk_get(&pdev->dev, "bclk_ext");
+ if (IS_ERR(dev->clks_dac_bclk))
+ return PTR_ERR(dev->clks_dac_bclk);
+
+ dev->clks_dac_lrck = devm_clk_get(&pdev->dev, "lrck_ext");
+ if (IS_ERR(dev->clks_dac_lrck))
+ return PTR_ERR(dev->clks_dac_lrck);
+
ret = clk_prepare_enable(dev->clks_audroot);
if (ret) {
dev_err(&pdev->dev, "%s: failed to enable clks_audroot\n", __func__);
CLK_ADC_LRCLK,
CLK_ADC_RX_BCLK,
CLK_ADC_RX_LRCK,
+ CLK_ADC_MCLK,
+ CLK_ADC_BCLK_EXT,
+ CLK_ADC_LRCK_EXT,
CLK_AUDIO_NUM,
};
struct clk *clks_mclk_out;
struct clk *clks_apb0;
struct clk *clks_4ch_apb;
+ struct clk *clks_dac_bclk;
+ struct clk *clks_dac_lrck;
struct reset_control *rstc_rx;
struct reset_control *rstc_ch0;
struct reset_control *rstc_ch1;
-//SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0
/*
* SPDIF driver for the StarFive JH7110 SoC
*
unsigned int format;
unsigned int tsamplerate;
unsigned int mclk;
+ unsigned int audio_root;
int ret;
channels = params_channels(params);
format = params_format(params);
switch (channels) {
+ case 1:
+ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+ SPDIF_CHANNEL_MODE, SPDIF_CHANNEL_MODE);
+ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+ SPDIF_DUPLICATE, SPDIF_DUPLICATE);
+ spdif->channels = false;
+ break;
case 2:
+ regmap_update_bits(spdif->regmap, SPDIF_CTRL,
+ SPDIF_CHANNEL_MODE, 0);
+ spdif->channels = true;
break;
default:
dev_err(dai->dev, "invalid channels number\n");
switch (format) {
case SNDRV_PCM_FORMAT_S16_LE:
case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
case SNDRV_PCM_FORMAT_S32_LE:
break;
default:
return -EINVAL;
}
+ audio_root = 204800000;
switch (rate) {
case 8000:
mclk = 4096000;
mclk = 8192000;
break;
case 22050:
+ audio_root = 153600000;
mclk = 11289600;
break;
default:
return -EINVAL;
}
+ ret = clk_set_rate(spdif->audio_root, audio_root);
+ if (ret) {
+ dev_err(dai->dev, "failed to set audio_root rate :%d\n", ret);
+ return ret;
+ }
+ dev_dbg(dai->dev, "audio_root get rate:%ld\n",
+ clk_get_rate(spdif->audio_root));
+
ret = clk_set_rate(spdif->mclk_inner, mclk);
if (ret) {
- dev_err(dai->dev, "failed to set rate for spdif mclk_inner ret=%d\n", ret);
+ dev_err(dai->dev, "failed to set mclk_inner rate :%d\n", ret);
return ret;
}
+ mclk = clk_get_rate(spdif->mclk_inner);
+ dev_dbg(dai->dev, "mclk_inner get rate:%d\n", mclk);
/* (FCLK)4096000/128=32000 */
- tsamplerate = (32000 + rate/2)/rate - 1;
-
+ tsamplerate = (mclk / 128 + rate / 2) / rate - 1;
if (tsamplerate < 3)
tsamplerate = 3;
static struct clk_bulk_data clks[] = {
{ .id = "spdif-apb" }, /* clock-names in dts file */
{ .id = "spdif-core" },
- { .id = "apb0" },
{ .id = "audroot" },
{ .id = "mclk_inner"},
};
spdif->spdif_apb = clks[0].clk;
spdif->spdif_core = clks[1].clk;
- spdif->apb0_clk = clks[2].clk;
- spdif->audio_root = clks[3].clk;
- spdif->mclk_inner = clks[4].clk;
+ spdif->audio_root = clks[2].clk;
+ spdif->mclk_inner = clks[3].clk;
return ret;
}
{ .id = "rst_apb" },
};
int ret = devm_reset_control_bulk_get_exclusive(&pdev->dev, ARRAY_SIZE(resets), resets);
+
if (ret)
return ret;
goto disable_core_clk;
}
- ret = clk_prepare_enable(spdif->apb0_clk);
- if (ret) {
- dev_err(&pdev->dev, "failed to prepare enable apb0_clk\n");
- goto disable_apb0_clk;
- }
-
- ret = clk_prepare_enable(spdif->audio_root);
- if (ret) {
- dev_err(&pdev->dev, "failed to prepare enable spdif->audio_root\n");
- goto disable_audroot_clk;
- }
-
ret = clk_set_rate(spdif->audio_root, 204800000);
if (ret) {
dev_err(&pdev->dev, "failed to set rate for spdif audroot ret=%d\n", ret);
- goto disable_audroot_clk;
- }
-
- ret = clk_prepare_enable(spdif->mclk_inner);
- if (ret) {
- dev_err(&pdev->dev, "failed to prepare enable spdif->mclk_inner\n");
- goto disable_mclk_clk;
+ goto disable_core_clk;
}
ret = clk_set_rate(spdif->mclk_inner, 8192000);
if (ret) {
dev_err(&pdev->dev, "failed to set rate for spdif mclk_inner ret=%d\n", ret);
- goto disable_mclk_clk;
+ goto disable_core_clk;
}
dev_dbg(&pdev->dev, "spdif->spdif_apb = %lu\n", clk_get_rate(spdif->spdif_apb));
dev_dbg(&pdev->dev, "spdif->spdif_core = %lu\n", clk_get_rate(spdif->spdif_core));
- dev_dbg(&pdev->dev, "spdif->apb0_clk = %lu\n", clk_get_rate(spdif->apb0_clk));
ret = reset_control_deassert(spdif->rst_apb);
if (ret) {
dev_err(&pdev->dev, "failed to deassert apb\n");
- goto disable_mclk_clk;
+ goto disable_core_clk;
}
return 0;
-disable_mclk_clk:
- clk_disable_unprepare(spdif->mclk_inner);
-disable_audroot_clk:
- clk_disable_unprepare(spdif->audio_root);
-disable_apb0_clk:
- clk_disable_unprepare(spdif->apb0_clk);
disable_core_clk:
clk_disable_unprepare(spdif->spdif_core);
disable_apb_clk:
.probe = sf_spdif_dai_probe,
.playback = {
.stream_name = "Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SF_PCM_RATE_8000_22050,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S32_LE,
- },
- .capture = {
- .stream_name = "Capture",
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.rates = SF_PCM_RATE_8000_22050,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &sf_spdif_dai_ops,
-//SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* SPDIF driver for the StarFive JH7110 SoC
*
SPDIF_FIOF_AEMPTY | SPDIF_FIFO_FULL | \
SPDIF_FIFO_AFULL | SPDIF_SYNCERR | \
SPDIF_LOCK | SPDIF_BLOCK_BEGIN)
-
+
#define SPDIF_ERROR_INT_STATUS (SPDIF_PARITYO | \
SPDIF_TDATA_UNDERR | SPDIF_RDATA_OVRERR)
#define SPDIF_FIFO_INT_STATUS (SPDIF_FIFO_EMPTY | SPDIF_FIOF_AEMPTY | \
#define SPDIF_BEGIN_FLAG (1<<30) /* 1:start a new block */
#define SPDIF_RIGHT_LEFT (1<<31) /* 1:left channel received and tx into FIFO; 0:right channel received and tx into FIFO */
-#define BIT8TO20MASK 0x1FFF
+#define BIT8TO20MASK 0x1FFF
#define ALLBITMASK 0xFFFFFFFF
#define SPDIF_STAT (SPDIF_PARITY_FLAG | SPDIF_UNDERR_FLAG | \
bool *period_elapsed, snd_pcm_format_t format);
snd_pcm_format_t format;
+ bool channels;
unsigned int tx_ptr;
unsigned int rx_ptr;
- struct clk* spdif_apb;
- struct clk* spdif_core;
- struct clk* apb0_clk;
- struct clk* audio_root;
- struct clk* mclk_inner;
+ struct clk *spdif_apb;
+ struct clk *spdif_core;
+ struct clk *audio_root;
+ struct clk *mclk_inner;
struct reset_control *rst_apb;
struct snd_dmaengine_dai_dma_data dma_data;
-/**
- ******************************************************************************
- * @file sf_spdif_pcm.c
- * @author StarFive Technology
- * @version V1.0
- * @date 05/27/2021
- * @brief
- ******************************************************************************
- * @copy
- *
- * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
- * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
- * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
- * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
- * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
- * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
- *
- * <h2><center>© COPYRIGHT 20120 Shanghai StarFive Technology Co., Ltd. </center></h2>
- */
+// SPDX-License-Identifier: GPL-2.0
+/*
+ ******************************************************************************
+ * @file sf_spdif_pcm.c
+ * @author StarFive Technology
+ * @version V1.0
+ * @date 05/27/2021
+ * @brief
+ ******************************************************************************
+ * @copy
+ *
+ * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+ * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
+ * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
+ * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
+ * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+ * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+ *
+ * <h2><center>© COPYRIGHT 20120 Shanghai StarFive Technology Co., Ltd. </center></h2>
+ */
#include <linux/io.h>
#include <linux/rcupdate.h>
#define PERIOD_BYTES_MIN 4096
#define PERIODS_MIN 2
-static unsigned int sf_spdif_pcm_tx(struct sf_spdif_dev *dev,
- struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
- bool *period_elapsed, snd_pcm_format_t format)
-{
- const u16 (*p16)[2] = (void *)runtime->dma_area;
- const u32 (*p32)[2] = (void *)runtime->dma_area;
+static unsigned int sf_spdif_pcm_tx(struct sf_spdif_dev *dev,
+ struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
+ bool *period_elapsed, snd_pcm_format_t format)
+{
u32 data[2];
- unsigned int period_pos = tx_ptr % runtime->period_size;
- int i;
-
- for (i = 0; i < dev->fifo_th; i++) {
- if (SNDRV_PCM_FORMAT_S16_LE == format) {
- data[0] = p16[tx_ptr][0];
- data[1] = p16[tx_ptr][1];
- data[0] = data[0]<<8;
- data[1] = data[1]<<8;
- } else if (SNDRV_PCM_FORMAT_S24_LE == format) {
- data[0] = p32[tx_ptr][0];
- data[1] = p32[tx_ptr][1];
- } else if (SNDRV_PCM_FORMAT_S32_LE == format) {
- data[0] = p32[tx_ptr][0];
- data[1] = p32[tx_ptr][1];
- data[0] = data[0]>>8;
- data[1] = data[1]>>8;
+ unsigned int period_pos = tx_ptr % runtime->period_size;
+ int i;
+
+ /* two- channel and signal-channel mode */
+ if (dev->channels) {
+ const u16 (*p16)[2] = (void *)runtime->dma_area;
+ const u32 (*p32)[2] = (void *)runtime->dma_area;
+
+ for (i = 0; i < dev->fifo_th; i++) {
+ if (format == SNDRV_PCM_FORMAT_S16_LE) {
+ data[0] = p16[tx_ptr][0];
+ data[0] = data[0]<<8;
+ data[0] &= 0x00ffff00;
+ data[1] = p16[tx_ptr][1];
+ data[1] = data[1]<<8;
+ data[1] &= 0x00ffff00;
+ } else if (format == SNDRV_PCM_FORMAT_S24_LE) {
+ data[0] = p32[tx_ptr][0];
+ data[1] = p32[tx_ptr][1];
+
+ /*
+ * To adapt S24_3LE and ALSA pass parameter of S24_LE.
+ * operation of S24_LE should be same to S24_3LE.
+ * So it would wrong when playback S24_LE file.
+ * when want to playback S24_LE file, should add in there:
+ * data[0] = data[0]>>8;
+ * data[1] = data[1]>>8;
+ */
+
+ data[0] &= 0x00ffffff;
+ data[1] &= 0x00ffffff;
+ } else if (format == SNDRV_PCM_FORMAT_S24_3LE) {
+ data[0] = p32[tx_ptr][0];
+ data[1] = p32[tx_ptr][1];
+ data[0] &= 0x00ffffff;
+ data[1] &= 0x00ffffff;
+ } else if (format == SNDRV_PCM_FORMAT_S32_LE) {
+ data[0] = p32[tx_ptr][0];
+ data[0] = data[0]>>8;
+ data[1] = p32[tx_ptr][1];
+ data[1] = data[1]>>8;
+ }
+
+ iowrite32(data[0], dev->spdif_base + SPDIF_FIFO_ADDR);
+ iowrite32(data[1], dev->spdif_base + SPDIF_FIFO_ADDR);
+ period_pos++;
+ if (++tx_ptr >= runtime->buffer_size)
+ tx_ptr = 0;
}
-
- iowrite32(data[0], dev->spdif_base + SPDIF_FIFO_ADDR);
- iowrite32(data[1], dev->spdif_base + SPDIF_FIFO_ADDR);
- period_pos++;
- if (++tx_ptr >= runtime->buffer_size) {
- tx_ptr = 0;
+ } else {
+ const u16 (*p16) = (void *)runtime->dma_area;
+ const u32 (*p32) = (void *)runtime->dma_area;
+
+ for (i = 0; i < dev->fifo_th; i++) {
+ if (format == SNDRV_PCM_FORMAT_S16_LE) {
+ data[0] = p16[tx_ptr];
+ data[0] = data[0]<<8;
+ data[0] &= 0x00ffff00;
+ } else if (format == SNDRV_PCM_FORMAT_S24_LE ||
+ format == SNDRV_PCM_FORMAT_S24_3LE) {
+ data[0] = p32[tx_ptr];
+ data[0] &= 0x00ffffff;
+ } else if (format == SNDRV_PCM_FORMAT_S32_LE) {
+ data[0] = p32[tx_ptr];
+ data[0] = data[0]>>8;
+ }
+
+ iowrite32(data[0], dev->spdif_base + SPDIF_FIFO_ADDR);
+ period_pos++;
+ if (++tx_ptr >= runtime->buffer_size)
+ tx_ptr = 0;
}
- }
+ }
- *period_elapsed = period_pos >= runtime->period_size;
- return tx_ptr;
+ *period_elapsed = period_pos >= runtime->period_size;
+ return tx_ptr;
}
-static unsigned int sf_spdif_pcm_rx(struct sf_spdif_dev *dev,
- struct snd_pcm_runtime *runtime, unsigned int rx_ptr,
- bool *period_elapsed, snd_pcm_format_t format)
-{
- u16 (*p16)[2] = (void *)runtime->dma_area;
- u32 (*p32)[2] = (void *)runtime->dma_area;
+static unsigned int sf_spdif_pcm_rx(struct sf_spdif_dev *dev,
+ struct snd_pcm_runtime *runtime, unsigned int rx_ptr,
+ bool *period_elapsed, snd_pcm_format_t format)
+{
+ u16 (*p16)[2] = (void *)runtime->dma_area;
+ u32 (*p32)[2] = (void *)runtime->dma_area;
u32 data[2];
- unsigned int period_pos = rx_ptr % runtime->period_size;
- int i;
+ unsigned int period_pos = rx_ptr % runtime->period_size;
+ int i;
- for (i = 0; i < dev->fifo_th; i++) {
+ for (i = 0; i < dev->fifo_th; i++) {
data[0] = ioread32(dev->spdif_base + SPDIF_FIFO_ADDR);
data[1] = ioread32(dev->spdif_base + SPDIF_FIFO_ADDR);
- if (SNDRV_PCM_FORMAT_S16_LE == format) {
+ if (format == SNDRV_PCM_FORMAT_S16_LE) {
p16[rx_ptr][0] = data[0]>>8;
p16[rx_ptr][1] = data[1]>>8;
- } else if (SNDRV_PCM_FORMAT_S24_LE == format) {
+ } else if (format == SNDRV_PCM_FORMAT_S24_LE) {
p32[rx_ptr][0] = data[0];
p32[rx_ptr][1] = data[1];
- } else if (SNDRV_PCM_FORMAT_S32_LE == format) {
+ } else if (format == SNDRV_PCM_FORMAT_S32_LE) {
p32[rx_ptr][0] = data[0]<<8;
p32[rx_ptr][1] = data[1]<<8;
- }
-
- period_pos++;
- if (++rx_ptr >= runtime->buffer_size)
- rx_ptr = 0;
- }
-
- *period_elapsed = period_pos >= runtime->period_size;
- return rx_ptr;
+ }
+
+ period_pos++;
+ if (++rx_ptr >= runtime->buffer_size)
+ rx_ptr = 0;
+ }
+
+ *period_elapsed = period_pos >= runtime->period_size;
+ return rx_ptr;
}
static const struct snd_pcm_hardware sf_pcm_hardware = {
.rate_max = 48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
SNDRV_PCM_FMTBIT_S32_LE,
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = BUFFER_BYTES_MAX,
.period_bytes_min = PERIOD_BYTES_MIN,
struct sf_spdif_dev *dev = runtime->private_data;
switch (params_channels(hw_params)) {
+ case 1:
case 2:
break;
default:
switch (dev->format) {
case SNDRV_PCM_FORMAT_S16_LE:
case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
case SNDRV_PCM_FORMAT_S32_LE:
break;
default:
struct sf_spdif_dev *dev = runtime->private_data;
snd_pcm_uframes_t pos;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
pos = READ_ONCE(dev->tx_ptr);
- }
- else {
+ else
pos = READ_ONCE(dev->rx_ptr);
- }
return pos < runtime->buffer_size ? pos : 0;
}
return 0;
}
-
+
static const struct snd_soc_component_driver sf_pcm_component = {
.open = sf_pcm_open,
.close = sf_pcm_close,
/*
* TDM driver for the StarFive JH7110 SoC
*
- * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ * Copyright (C) 2022 StarFive Technology Co., Ltd.
*/
#include <linux/clk.h>
#include <linux/device.h>
{
u32 datarx, datatx;
- sf_tdm_stop(dev, substream);
sf_tdm_contrl(dev);
sf_tdm_syncdiv(dev);
int channels;
int ret;
+ channels = params_channels(params);
+ data_width = params_width(params);
+
dev->samplerate = params_rate(params);
switch (dev->samplerate) {
- /* There is a issue with 8k sample rate */
-/*
+ /* There are some limitation when using 8k sample rate */
case 8000:
- mclk_rate = 11289600; //sysclk
- dev->pcmclk = 352800; //bit clock
+ mclk_rate = 12288000;
+ dev->pcmclk = 512000;
+ if ((data_width == 16) || (channels == 1)) {
+ pr_err("TDM: not support 16bit or 1-channel when using 8k sample rate\n");
+ return -EINVAL;
+ }
break;
-*/
case 11025:
mclk_rate = 11289600; //sysclk
dev->pcmclk = 352800; //bit clock, for 16-bit
return -EINVAL;
}
- data_width = params_width(params);
- channels = params_channels(params);
dev->pcmclk = channels * dev->samplerate * data_width;
switch (params_format(params)) {
dma_bus_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
break;
- /* There are some issue with 24-bit and 32-bit */
-/*
- case SNDRV_PCM_FORMAT_S24_LE:
- chan_wl = TDM_24BIT_WORD_LEN;
- chan_sl = TDM_32BIT_SLOT_LEN;
- dma_bus_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- break;
-
case SNDRV_PCM_FORMAT_S32_LE:
chan_wl = TDM_32BIT_WORD_LEN;
chan_sl = TDM_32BIT_SLOT_LEN;
dma_bus_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
break;
-*/
default:
dev_err(dev->dev, "tdm: unsupported PCM fmt");
return 0;
}
-#define SF_TDM_RATES (SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
- SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | \
- SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define SF_TDM_RATES SNDRV_PCM_RATE_8000_48000
#define SF_TDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
.channels_min = 1,
.channels_max = 8,
.rates = SF_TDM_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SF_TDM_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 8,
.rates = SF_TDM_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SF_TDM_FORMATS,
},
.ops = &sf_tdm_dai_ops,
.probe = sf_tdm_dai_probe,
dev->rx.wl = dev->tx.wl = TDM_16BIT_WORD_LEN;
dev->rx.sscale = dev->tx.sscale = 2;
dev->rx.lrj = dev->tx.lrj = TDM_LEFT_JUSTIFT;
- dev->samplerate = 16000;
- dev->pcmclk = 512000;
dev->play_dma_data.addr = TDM_FIFO;
dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;