[PATCH v13 06/14] drm/mediatek: Add HDMI support

Philipp Zabel p.zabel at pengutronix.de
Mon Mar 21 10:45:29 PDT 2016


Hi Daniel, Jie,

Am Mittwoch, den 09.03.2016, 21:52 +0800 schrieb Daniel Kurtz:
> Hi Philipp & Jie,
> 
> Sorry I only now had a chance to dig deeper and review the HDMI driver.

I wish you had had a chance to do this earlier, But better now than
after the merge. I'll split the HDMI patches from the others in the next
round.

> Lots of comments inline below...
>
> On Tue, Mar 8, 2016 at 9:27 PM, Philipp Zabel <p.zabel at pengutronix.de> wrote:
[...]
> > diff --git a/drivers/gpu/drm/mediatek/mtk_cec.c b/drivers/gpu/drm/mediatek/mtk_cec.c
> > new file mode 100644
> > index 0000000..cba3647
> > --- /dev/null
> > +++ b/drivers/gpu/drm/mediatek/mtk_cec.c
> > +void mtk_cec_set_hpd_event(struct device *dev,
> > +                          void (*hpd_event)(bool hpd, struct device *dev),
> > +                          struct device *hdmi_dev)
> > +{
> > +       struct mtk_cec *cec = dev_get_drvdata(dev);
> > +
> > +       cec->hdmi_dev = hdmi_dev;
> > +       cec->hpd_event = hpd_event;
> 
> Lock this so to prevent race with the irq?

Yes.

[...]
> > +int mtk_cec_irq(struct device *dev)
> 
> AFAICT, this function is never used.

Correct, since the IRQ number is not exported to the sound drivers
anymore, I can drop it now.

[...]
> > +static void mtk_cec_htplg_irq_enable(struct mtk_cec *cec)
> > +{
> > +       mtk_cec_mask(cec, CEC_CKGEN, 0, PDN);
> > +       mtk_cec_mask(cec, CEC_CKGEN, CEC_32K_PDN, CEC_32K_PDN);
> > +       mtk_cec_mask(cec, RX_GEN_WD, HDMI_PORD_INT_32K_CLR,
> > +                    HDMI_PORD_INT_32K_CLR);
> > +       mtk_cec_mask(cec, RX_GEN_WD, RX_INT_32K_CLR, RX_INT_32K_CLR);
> > +       mtk_cec_mask(cec, RX_GEN_WD, HDMI_HTPLG_INT_32K_CLR,
> > +                    HDMI_HTPLG_INT_32K_CLR);
> > +       mtk_cec_mask(cec, RX_GEN_WD, 0, HDMI_PORD_INT_32K_CLR);
> > +       mtk_cec_mask(cec, RX_GEN_WD, 0, RX_INT_32K_CLR);
> > +       mtk_cec_mask(cec, RX_GEN_WD, 0, HDMI_HTPLG_INT_32K_CLR);
> > +       mtk_cec_mask(cec, RX_GEN_WD, 0, HDMI_PORD_INT_32K_EN);
> > +       mtk_cec_mask(cec, RX_GEN_WD, 0, RX_INT_32K_EN);
> > +       mtk_cec_mask(cec, RX_GEN_WD, 0, HDMI_HTPLG_INT_32K_EN);
> 
> This is a bit wasteful.  Can you just clear all of these bits in a single write?
> (this applies to this entire file).

I think so. If there are no problems, I'll combine them into a single
update per register.

> > +
> > +       mtk_cec_mask(cec, RX_EVENT, HDMI_PORD_INT_EN, HDMI_PORD_INT_EN);
> > +       mtk_cec_mask(cec, RX_EVENT, HDMI_HTPLG_INT_EN, HDMI_HTPLG_INT_EN);
> > +}
> > +
> > +static void mtk_cec_htplg_irq_disable(struct mtk_cec *cec)
> > +{
> 
> Why does irq_enable do so much more work than irq_disable?

It also clears the interrupt status and sets the clock. I'll move the
initialization out of this function.

> > +       mtk_cec_mask(cec, RX_EVENT, 0, HDMI_PORD_INT_EN);
> > +       mtk_cec_mask(cec, RX_EVENT, 0, HDMI_HTPLG_INT_EN);
> > +}
> > +
> > +static void mtk_cec_clear_htplg_irq(struct mtk_cec *cec)
> > +{
> > +       mtk_cec_mask(cec, TR_CONFIG, CLEAR_CEC_IRQ, CLEAR_CEC_IRQ);
> > +       mtk_cec_mask(cec, NORMAL_INT_CTRL, HDMI_HTPLG_INT_CLR,
> > +                    HDMI_HTPLG_INT_CLR);
> > +       mtk_cec_mask(cec, NORMAL_INT_CTRL, HDMI_PORD_INT_CLR,
> > +                    HDMI_PORD_INT_CLR);
> > +       mtk_cec_mask(cec, NORMAL_INT_CTRL, HDMI_FULL_INT_CLR,
> > +                    HDMI_FULL_INT_CLR);
> > +       mtk_cec_mask(cec, RX_GEN_WD, HDMI_PORD_INT_32K_CLR,
> > +                    HDMI_PORD_INT_32K_CLR);
> > +       mtk_cec_mask(cec, RX_GEN_WD, RX_INT_32K_CLR, RX_INT_32K_CLR);
> > +       mtk_cec_mask(cec, RX_GEN_WD, HDMI_HTPLG_INT_32K_CLR,
> > +                    HDMI_HTPLG_INT_32K_CLR);
> > +       udelay(5);
> 
> Do you really need this delay in the middle of the isr handler?

I can turn it into an usleep_range(5, 10). Whether the delay is needed
at all or how long it really has to be, I don't know.

[...]
> > +static irqreturn_t mtk_cec_htplg_isr_thread(int irq, void *arg)
> > +{
> > +       struct device *dev = arg;
> > +       struct mtk_cec *cec = dev_get_drvdata(dev);
> > +       bool hpd;
> > +
> > +       mtk_cec_clear_htplg_irq(cec);
> > +       hpd = mtk_cec_hpd_high(dev);
> > +
> > +       if (cec->hpd != hpd) {
> > +               dev_info(dev, "hotplug event!,cur hpd = %d, hpd = %d\n",
> > +                        cec->hpd, hpd);
> 
> dev_dbg if anything

Ok.

[...]
> > diff --git a/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c
> > new file mode 100644
> > index 0000000..ff661e0
> > --- /dev/null
> > +++ b/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c
[...]
> > +static void mtk_hdmi_bridge_disable(struct drm_bridge *bridge)
> > +{
> > +       struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
> > +
> > +       phy_power_off(hdmi->phy);
> > +       clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_HDMI_PIXEL]);
> > +       clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]);
> 
> As far as I can tell, __drm_helper_disable_unused_functions() doesn't
> check if crtc/encoder/bridge are disabled before calling the
> ->*en/disable*() callbacks.
>
> So, these clk_disable_unprepare() may be called with the HDMI already disabled,
> trigerring their WARN_ON().
> 
> So, perhaps we also need to track enabled/disabled state separately here in
> the driver.

I'll add that.

[...]
> > +       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +       hdmi->regs = devm_ioremap_resource(dev, mem);
> > +       if (IS_ERR(hdmi->regs)) {
> > +               dev_err(dev, "Failed to ioremap hdmi_shell: %ld\n",
> > +                       PTR_ERR(hdmi->regs));
> 
> What is hdmi_shell?

I don't know, I assumed it's just the name of the HDMI control register
space.

> In any case, I don't think you need to print anything here.

I'll drop the printk.

[...]
> > +static int mtk_drm_hdmi_remove(struct platform_device *pdev)
> > +{
> > +       struct mtk_hdmi *hdmi = platform_get_drvdata(pdev);
> > +
> > +       drm_bridge_remove(&hdmi->bridge);
> > +       platform_device_unregister(hdmi->audio_pdev);
> 
> audio_pdev is not set in this patch.
> Is there more audio stuff that should be removed from this patch?

I suppose I could also remove the hdmi_audio_param struct and simplify
mtk_hdmi_aud_set_input() a bit.

> > +       platform_set_drvdata(pdev, NULL);
> 
> I don't think this is necessary.

Right.

[...]
> > +static int mtk_hdmi_resume(struct device *dev)
> > +{
> > +       struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
> > +       int ret = 0;
> > +
> > +       ret = mtk_hdmi_clk_enable_audio(hdmi);
> > +       if (ret) {
> > +               dev_err(dev, "hdmi resume failed!\n");
> > +               return ret;
> > +       }
> > +
> > +       mtk_hdmi_power_on(hdmi);
> > +       mtk_hdmi_output_set_display_mode(hdmi, &hdmi->mode);
> > +       phy_power_on(hdmi->phy);
> > +       dev_dbg(dev, "hdmi resume success!\n");
> > +       return 0;
> > +}
> > +#endif
> > +static SIMPLE_DEV_PM_OPS(mtk_hdmi_pm_ops,
> > +                        mtk_hdmi_suspend, mtk_hdmi_resume);
> 
> I do not think these suspend/resume ops are needed.
> The MTK DRM driver turn off connectors at suspend, and re-enables them
> at resume.

I have to move the audio clock enabling into the bridge enable/disable
to drop these completely, not sure yet if that will have any side
effects.

[...]
> > diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
> > new file mode 100644
> > index 0000000..30ec7b5
> > --- /dev/null
> > +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c
> 
> Hmm... what is the value in splitting this out into its own separate mtk_hdmi.c?

If nobody minds, I'll happily merge mtk_drm_hdmi_drv.c, mtk_hdmi.c and
mtk_hdmi_hw.c. The downside is that the file is now >1.8k lines, but I
think the amount of newly static functions is worth it.

[...]
> > +static int mtk_hdmi_aud_set_input(struct mtk_hdmi *hdmi)
> > +{
[...]
> > +       mtk_hdmi_hw_aud_set_high_bitrate(hdmi, false);
> > +       mtk_hdmi_phy_aud_dst_normal_double_enable(hdmi, false);
> > +       mtk_hdmi_hw_aud_dst_enable(hdmi, false);
> 
> These three all change the same register.  Combine them into a single helper
> function that just writes GRL_AUDIO_CFG once.

Ok.

[...]
> > +static int mtk_hdmi_aud_set_src(struct mtk_hdmi *hdmi,
> > +                               struct drm_display_mode *display_mode)
> > +{
> > +       mtk_hdmi_aud_on_off_hw_ncts(hdmi, false);
> > +
> > +       if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_I2S) {
> > +               switch (hdmi->aud_param.aud_hdmi_fs) {
> > +               case HDMI_AUDIO_SAMPLE_FREQUENCY_32000:
> > +               case HDMI_AUDIO_SAMPLE_FREQUENCY_44100:
> > +               case HDMI_AUDIO_SAMPLE_FREQUENCY_48000:
> > +               case HDMI_AUDIO_SAMPLE_FREQUENCY_88200:
> > +               case HDMI_AUDIO_SAMPLE_FREQUENCY_96000:
> > +                       mtk_hdmi_hw_aud_src_off(hdmi);
> > +                       /* mtk_hdmi_hw_aud_src_enable(hdmi, false); */
> 
> why is this commented out?

Not yet implemented at this point. I'll remove it.

> > +                       mtk_hdmi_hw_aud_set_mclk(
> > +                       hdmi,
> > +                       hdmi->aud_param.aud_mclk);
> 
> indentation is funny here

Will fix, and the following similar issues, too.

> > +                       mtk_hdmi_hw_aud_src_off(hdmi);
> > +                       mtk_hdmi_hw_aud_set_mclk(hdmi,
> > +                                                HDMI_AUD_MCLK_128FS);
> > +                       mtk_hdmi_hw_aud_aclk_inv_enable(hdmi, false);
> 
> These clauses all seem to do the same thing; only the mclk parameter is
> different.  Can you refactor them into a helper function?

Yes, I'll have to integrate a few changes from the
"drm/mediatek: hdmi: Add audio interface to the hdmi-codec driver"
patch here.

[...]
> > +int mtk_hdmi_hpd_high(struct mtk_hdmi *hdmi)
> > +{
> > +       return hdmi->cec_dev ? mtk_cec_hpd_high(hdmi->cec_dev) : false;
> 
> I don't think we would get this far if cec_dev was NULL.

You are right, the HDMI device defers probing until it can find the CEC
device. I'll drop mtk_hdmi_hpd_high and call mtk_cec_hpd_high directly.

> > +int mtk_hdmi_output_init(struct mtk_hdmi *hdmi)
> > +{
> > +       struct hdmi_audio_param *aud_param = &hdmi->aud_param;
> > +
> > +       if (hdmi->init)
> > +               return -EINVAL;
> 
> This check is not needed; this function is only called once, during probe.
> In fact, I don't think the "->init" field is needed at all.

I agree. mtk_hdmi_output_init is called in probe before drm_bridge_add.
mtk_hdmi_output_set_display_mode, which this is supposed to protect, is
only called from the bridge_enable callback. Another indication that the
code is split over too many files.

[...]
> > +int mtk_hdmi_output_set_display_mode(struct mtk_hdmi *hdmi,
> > +                                    struct drm_display_mode *mode)
> > +{
> > +       int ret;
> > +
> > +       if (!hdmi->init) {
> 
> Is this possible?

No. I'll remove it.

> > +               dev_err(hdmi->dev, "doesn't init hdmi control context!\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       mtk_hdmi_hw_vid_black(hdmi, true);
> > +       mtk_hdmi_hw_aud_mute(hdmi, true);
> > +       mtk_hdmi_setup_av_mute_packet(hdmi);
> > +       phy_power_off(hdmi->phy);
> > +
> > +       ret = mtk_hdmi_video_change_vpll(hdmi,
> > +                                        mode->clock * 1000);
> > +       if (ret) {
> > +               dev_err(hdmi->dev, "Failed to set vpll: %d\n", ret);
> 
> cleanup on error?

The only things that happen before are vid_black/aud_mute and PHY power
off. I assume neither can reasonably be reverted if we can't even set
the PLL correctly?

> > +               return ret;
> > +       }
> > +       mtk_hdmi_video_set_display_mode(hdmi, mode);
> > +
> > +       phy_power_on(hdmi->phy);
> > +       mtk_hdmi_aud_output_config(hdmi, mode);
> > +
> > +       mtk_hdmi_setup_audio_infoframe(hdmi);
> > +       mtk_hdmi_setup_avi_infoframe(hdmi, mode);
> > +       mtk_hdmi_setup_spd_infoframe(hdmi, "mediatek", "chromebook");
> 
> what?  No.  The "product" should refer to the MTK HDMI block.

I need a little help here. "mediatek", "On-chip HDMI"?
The Intel driver sets "intel", "Integrated gfx", for example.

> > diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.h b/drivers/gpu/drm/mediatek/mtk_hdmi.h
> > new file mode 100644
> > index 0000000..9403915
> > --- /dev/null
> > +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.h
[...]
> > +struct mtk_hdmi {
> > +       struct drm_bridge bridge;
> > +       struct drm_connector conn;
> > +       struct device *dev;
> > +       struct phy *phy;
> > +       struct device *cec_dev;
> > +       struct i2c_adapter *ddc_adpt;
> > +       struct clk *clk[MTK_HDMI_CLK_COUNT];
> > +#if defined(CONFIG_DEBUG_FS)
> > +       struct dentry *debugfs;
> > +#endif
> 
> Remove all of the debugfs stuff from this patch, since it isn't implemented.

Ok.

[...]
> > diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_drv.c b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_drv.c
> > new file mode 100644
> > index 0000000..22e5487
> > --- /dev/null
> > +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_drv.c
[...]
> > +struct mtk_hdmi_i2c {
> 
> Throughout this driver, I think we should:
> 
> s/mtk_hdmi_i2c/mtk_hdmi_ddc

I'm fine with that.

[...]
> > +static void ddcm_trigger_mode(struct mtk_hdmi_i2c *i2c, int mode)
> > +{
[...]
> > +       while (sif_bit_is_set(i2c, DDC_DDCMCTL1, DDCM_TRI)) {
> > +               timeout -= 2;
> > +               udelay(2);
> > +               if (timeout <= 0)
> > +                       break;
> > +       }
> 
> Use iopoll?

I'll replace the loop with readl_poll_timeout().

[...]
> > +static int mtk_hdmi_i2c_probe(struct platform_device *pdev)
> > +{
[...]
> > +       i2c->clk = devm_clk_get(dev, "ddc-i2c");
> > +       if (IS_ERR(i2c->clk)) {
> > +               dev_err(dev, "get ddc_clk failed : %p ,\n", i2c->clk);
> 
> nit, no space before ':'

Ok.

[...]
> > +       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +       i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
> > +       if (IS_ERR(i2c->regs)) {
> > +               dev_err(dev, "get memory source fail!\n");
> 
> nit: don't really need to print anything here.

Ok.

[...]
> > +       strlcpy(i2c->adap.name, "mediatek-hdmi-i2c", sizeof(i2c->adap.name));
> > +       i2c->adap.owner = THIS_MODULE;
> 
> i2c->adap.class = I2C_CLASS_DDC;

Ok.

> > +       i2c->adap.algo = &mtk_hdmi_i2c_algorithm;
> > +       i2c->adap.retries = 3;
> 
> why set this?

Jie, is there a known issue that made it necessary to enable the
automatic retries?

[...]
> > +       dev_dbg(dev, "i2c->adap: %p\n", &i2c->adap);
> > +       dev_dbg(dev, "i2c->clk: %p\n", i2c->clk);
> > +       dev_dbg(dev, "physical adr: 0x%llx, end: 0x%llx\n", mem->start,
> > +               mem->end);
> 
> remove these debugging lines.

Ok.

[...]
> > +static int mtk_hdmi_i2c_remove(struct platform_device *pdev)
> > +{
> > +       struct mtk_hdmi_i2c *i2c = platform_get_drvdata(pdev);
> > +
> > +       clk_disable_unprepare(i2c->clk);
> > +       i2c_del_adapter(&i2c->adap);
> 
> To match probe order, call i2c_del_adapter() first.

Ok.

[...]
> > diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_hw.c b/drivers/gpu/drm/mediatek/mtk_hdmi_hw.c
> > new file mode 100644
> > index 0000000..99c7ffc
> > --- /dev/null
> > +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_hw.c
> 
> What is the value in having this as a separate mtk_hdmi_hw.c file?
> If these functions were in mtk_hdmi.c, they could all be static, and
> the compiler
> could inline them away.

Yes, I'd prefer to merge all three into mtk_hdmi.c.

[...]
> > +#include "mtk_hdmi_hw.h"
> > +#include "mtk_hdmi_regs.h"
> > +#include "mtk_hdmi.h"
> 
> I think these usually go after system includes.

Ok.

[...]
> > +#define NCTS_BYTES          0x07
> 
> move above the functions

Ok.

[...]
> > +       switch (frame_type) {
> > +       case HDMI_INFOFRAME_TYPE_AVI:
> > +               ctrl_frame_en = CTRL_AVI_EN;
> > +               ctrl_reg = GRL_CTRL;
> > +               break;
> > +       case HDMI_INFOFRAME_TYPE_SPD:
> > +               ctrl_frame_en = CTRL_SPD_EN;
> > +               ctrl_reg = GRL_CTRL;
> > +               break;
> > +       case HDMI_INFOFRAME_TYPE_AUDIO:
> > +               ctrl_frame_en = CTRL_AUDIO_EN;
> > +               ctrl_reg = GRL_CTRL;
> > +               break;
> > +       case HDMI_INFOFRAME_TYPE_VENDOR:
> > +               ctrl_frame_en = VS_EN;
> > +               ctrl_reg = GRL_ACP_ISRC_CTRL;
> > +               break;
> > +       default:
> 
> Just fall through if none of the above?

These are the only four currently defined. I'll change frame_type to
enum hdmi_infoframe_type and drop the default label.

[...]
> > +void mtk_hdmi_hw_config_sys(struct mtk_hdmi *hdmi)
> > +{
> > +       regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20,
> > +                          HDMI_OUT_FIFO_EN | MHL_MODE_ON, 0);
> > +       mdelay(2);
> 
> Can this be msleep instead of mdelay?
> It is a bit rude to hog the CPU for 2 msec.

Yes this is ultimately called by .bridge_enable.
Same for the others two mdelay()s.

[...]
> > +void mtk_hdmi_hw_aud_set_bit_num(struct mtk_hdmi *hdmi,
> > +                                enum hdmi_audio_sample_size bit_num)
> > +{
> > +       u32 val = 0;
> 
> no 0 init

I'll use a switch statement below instead.

> > +
> > +       if (bit_num == HDMI_AUDIO_SAMPLE_SIZE_16)
> > +               val = AOUT_16BIT;
> > +       else if (bit_num == HDMI_AUDIO_SAMPLE_SIZE_20)
> > +               val = AOUT_20BIT;
> > +       else if (bit_num == HDMI_AUDIO_SAMPLE_SIZE_24)
> > +               val = AOUT_24BIT;
> > +
> > +       mtk_hdmi_mask(hdmi, GRL_AOUT_BNUM_SEL, val, 0x03);
> > +}
> > +
> > +void mtk_hdmi_hw_aud_set_i2s_fmt(struct mtk_hdmi *hdmi,
> > +                                enum hdmi_aud_i2s_fmt i2s_fmt)
> > +{
> > +       u32 val = 0;
> 
> no 0 init

Ok.

> > +
> > +       val = mtk_hdmi_read(hdmi, GRL_CFG0);
> > +       val &= ~0x33;
> 
> #define this mask

Ok.

[...]
> > +void mtk_hdmi_hw_aud_set_i2s_chan_num(struct mtk_hdmi *hdmi,
> > +                                     enum hdmi_aud_channel_type channel_type,
> > +                                     u8 channel_count)
> > +{
> > +       u8 val_1, val_2, val_3, val_4;
> 
> better:
>   u8 sw[3], uv;

Yes, I'll try to make this function a bit more readable.

> > +
> > +       if (channel_count == 2) {
> > +               val_1 = 0x04;
> > +               val_2 = 0x50;
> 
> Some #defines with meaningful names would be nice here.

I'll add a few defines. Also I notice that the ch_switch matrix
configuration is always the same.

[...]
> > +void mtk_hdmi_hw_aud_set_input_type(struct mtk_hdmi *hdmi,
> > +                                   enum hdmi_aud_input_type input_type)
> > +{
> > +       u32 val = 0;
> 
> no need to 0 init

Ok.

[...]
> > +void mtk_hdmi_hw_aud_set_channel_status(struct mtk_hdmi *hdmi,
> > +                                       u8 *l_chan_status, u8 *r_chan_status,
> > +                                       enum hdmi_audio_sample_frequency
> > +                                       aud_hdmi_fs)
> > +{
> > +       u8 l_status[5];
> > +       u8 r_status[5];
> > +       u8 val = 0;
> 
> no need to 0 init

Ok.

[...]
> > +       val = l_chan_status[4];
> > +       val |= ((~(l_status[3] & 0x0f)) << 4);
> > +       l_status[4] = val;
> > +       r_status[4] = val;
> > +
> > +       val = l_status[0];
> 
> nit: You don't need to bounce through val here.
> You can just write the *_status[n] value directly.

You are right, I have already fixed this in the later audio patches.
I'll reorganize this a bit.

[...]
> > +void mtk_hdmi_hw_aud_src_reenable(struct mtk_hdmi *hdmi)
> > +{
> > +       u32 val;
> > +
> > +       val = mtk_hdmi_read(hdmi, GRL_MIX_CTRL);
> > +       if (val & MIX_CTRL_SRC_EN) {
> > +               val &= ~MIX_CTRL_SRC_EN;
> > +               mtk_hdmi_write(hdmi, GRL_MIX_CTRL, val);
> > +               usleep_range(255, 512);
> 
> Those are some very precise values for a range...

Peculiar. If there are no objections, I'll change them to 250-500.

> > +               val |= MIX_CTRL_SRC_EN;
> > +               mtk_hdmi_write(hdmi, GRL_MIX_CTRL, val);
> > +       }
> > +}
> > +
> > +void mtk_hdmi_hw_aud_src_off(struct mtk_hdmi *hdmi)
> 
> Perhaps *_disable() would be more consistent.

Ok. Although the use of disable/reenable here seems to be inverted
compared to the common enable/disable pattern.

[...]
> > +void mtk_hdmi_hw_aud_aclk_inv_enable(struct mtk_hdmi *hdmi, bool enable)
> 
> nit: I prefer explicit _enable() and _disable() functions w/out the
> 'enable' parameter.

This function doesn't enable/disable a clock, it justs sets the ACLK_INV
bit, which supposedly determines whether the inverse audio clock is used
to latch data when downsampling 192k to 48k in I2S. I'll rename this to
mtk_hdmi_hw_aud_set_aclk_inv(). Or I could drop this function when
merging hdmi_hw.c into hdmi.c and use something like
	mtk_hdmi_clear_bits(hdmi, GRL_CFG2, CFG2_ACLK_INV)
directly.

[...]
> > +static unsigned int hdmi_expected_cts(unsigned int audio_sample_rate,
> > +                                     unsigned int tmds_clock, unsigned int n)
> > +{
> > +       return DIV_ROUND_CLOSEST_ULL((u64)hdmi_mode_clock_to_hz(tmds_clock) * n,
> > +                                    128 * audio_sample_rate);
> > +}
> 
> Doug Anderson may have some opinions about how N & CTS are computed.

I intend to use Arnaud's N/CTS helpers instead, when they are merged.

[...]
> > +static void do_hdmi_hw_aud_set_ncts(struct mtk_hdmi *hdmi, unsigned int n,
> > +                                   unsigned int cts)
> > +{
> > +       unsigned char val[NCTS_BYTES];
> > +       int i;
> > +
> > +       mtk_hdmi_write(hdmi, GRL_NCTS, 0);
> > +       mtk_hdmi_write(hdmi, GRL_NCTS, 0);
> > +       mtk_hdmi_write(hdmi, GRL_NCTS, 0);
> > +       memset(val, 0, sizeof(val));
> 
> not necessary, since you fill in all 7 bytes anyway.

Ok.

> > +
> > +       val[0] = (cts >> 24) & 0xff;
> > +       val[1] = (cts >> 16) & 0xff;
> > +       val[2] = (cts >> 8) & 0xff;
> > +       val[3] = cts & 0xff;
> > +
> > +       val[4] = (n >> 16) & 0xff;
> > +       val[5] = (n >> 8) & 0xff;
> > +       val[6] = n & 0xff;
> 
> all of these "& 0xff" are not needed, since val is an unsigned char array.

Even so, this makes it clear what happens. I expect the compiler to
optimize them away if possible.

> > +
> > +       for (i = 0; i < NCTS_BYTES; i++)
> > +               mtk_hdmi_write(hdmi, GRL_NCTS, val[i]);
> 
> What an interesting design.  We write all 10 bytes to the same register address?

That is what I am told.

> In this case, why bother with val at all?
> Just directly call mtk_hdmi_write() for each of the bytes above.

Yes, that would be better.

> > diff --git a/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c b/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c
> > new file mode 100644
> > index 0000000..5d9f07f
> > --- /dev/null
> > +++ b/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c
[...]
> > +static void mtk_hdmi_pll_unprepare(struct clk_hw *hw)
> > +{
> > +       struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
> > +
> > +       dev_dbg(hdmi_phy->dev, "prepare\n");
> 
> nit: "unprepare" (or just '"%s\n", __func__)', here and everywhere.

Ok.

> > +static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> > +                                unsigned long parent_rate)
> > +{
> > +       struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
> > +       unsigned int pre_div;
> > +       unsigned int div;
> > +
> > +       dev_dbg(hdmi_phy->dev, "set rate : %lu, parent: %lu\n", rate,
> 
> nit, no space before the first ':'.

Ok.

> > +static void mtk_hdmi_phy_enable_tmds(struct mtk_hdmi_phy *hdmi_phy)
> > +{
> > +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON3, RG_HDMITX_SER_EN,
> > +                         RG_HDMITX_SER_EN);
> 
> nit: lots of these calls might be more readable (and easier to maintain &
> review) if we used two helper functions:
> 
> mtk_hdmi_phy_set_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, u32 mask);
> mtk_hdmi_phy_clr_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, u32 mask);
> 
> and
> 
> mtk_hdmi_set_bits()
> mtk_hdmi_clr_bits()

It seems I'm too used to regmap_update_bits. I don't find separate
set/clear functions easier to read at all, and I stumble over the clr
abbreviation. I'll change to _set_bits and _clear_bits functions and
hope that I am in the minority.

[...]
> > +static struct phy_ops mtk_hdmi_phy_ops = {
> 
> static const

Ok. Thanks for the review!

regards
Philipp




More information about the Linux-mediatek mailing list