1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright 2017,2018 NXP
4 * Copyright 2019 Purism SPC
7 #include <linux/bitfield.h>
9 #include <linux/clk-provider.h>
10 #include <linux/delay.h>
11 #include <linux/firmware/imx/ipc.h>
12 #include <linux/firmware/imx/svc/misc.h>
14 #include <linux/kernel.h>
15 #include <linux/mfd/syscon.h>
16 #include <linux/module.h>
18 #include <linux/of_platform.h>
19 #include <linux/phy/phy.h>
20 #include <linux/platform_device.h>
21 #include <linux/regmap.h>
22 #include <dt-bindings/firmware/imx/rsrc.h>
24 /* Control and Status Registers(CSR) */
26 #define CCM_MASK GENMASK(7, 5)
27 #define CCM(n) FIELD_PREP(CCM_MASK, (n))
29 #define CA_MASK GENMASK(4, 2)
31 #define CA(n) FIELD_PREP(CA_MASK, (n))
33 #define LVDS_EN BIT(0)
36 #define DPHY_PD_DPHY 0x00
37 #define DPHY_M_PRG_HS_PREPARE 0x04
38 #define DPHY_MC_PRG_HS_PREPARE 0x08
39 #define DPHY_M_PRG_HS_ZERO 0x0c
40 #define DPHY_MC_PRG_HS_ZERO 0x10
41 #define DPHY_M_PRG_HS_TRAIL 0x14
42 #define DPHY_MC_PRG_HS_TRAIL 0x18
43 #define DPHY_PD_PLL 0x1c
48 #define DPHY_LOCK 0x30
49 #define DPHY_LOCK_BYP 0x34
50 #define DPHY_REG_BYPASS_PLL 0x4C
52 #define MBPS(x) ((x) * 1000000)
54 #define DATA_RATE_MAX_SPEED MBPS(1500)
55 #define DATA_RATE_MIN_SPEED MBPS(80)
57 #define PLL_LOCK_SLEEP 10
58 #define PLL_LOCK_TIMEOUT 1000
60 #define CN_BUF 0xcb7a89c0
63 ((x) < 32) ? 0xe0 | ((x) - 16) : \
64 ((x) < 64) ? 0xc0 | ((x) - 32) : \
65 ((x) < 128) ? 0x80 | ((x) - 64) : \
67 #define CN(x) (((x) == 1) ? 0x1f : (((CN_BUF) >> ((x) - 1)) & 0x1f))
68 #define CO(x) ((CO_BUF) >> (8 - (x)) & 0x03)
70 /* PHY power on is active low */
74 #define MIN_VCO_FREQ 640000000
75 #define MAX_VCO_FREQ 1500000000
77 #define MIN_LVDS_REFCLK_FREQ 24000000
78 #define MAX_LVDS_REFCLK_FREQ 150000000
80 enum mixel_dphy_devtype
{
85 struct mixel_dphy_devdata
{
91 bool is_combo
; /* MIPI DPHY and LVDS PHY combo */
94 static const struct mixel_dphy_devdata mixel_dphy_devdata
[] = {
97 .reg_auto_pd_en
= 0x3c,
100 .reg_rxhs_settle
= 0x48,
108 struct mixel_dphy_cfg
{
109 /* DPHY PLL parameters */
113 /* DPHY register values */
114 u8 mc_prg_hs_prepare
;
123 struct mixel_dphy_priv
{
124 struct mixel_dphy_cfg cfg
;
125 struct regmap
*regmap
;
126 struct regmap
*lvds_regmap
;
127 struct clk
*phy_ref_clk
;
128 const struct mixel_dphy_devdata
*devdata
;
129 struct imx_sc_ipc
*ipc_handle
;
134 static const struct regmap_config mixel_dphy_regmap_config
= {
138 .max_register
= DPHY_REG_BYPASS_PLL
,
142 static int phy_write(struct phy
*phy
, u32 value
, unsigned int reg
)
144 struct mixel_dphy_priv
*priv
= phy_get_drvdata(phy
);
147 ret
= regmap_write(priv
->regmap
, reg
, value
);
149 dev_err(&phy
->dev
, "Failed to write DPHY reg %d: %d\n", reg
,
155 * Find a ratio close to the desired one using continued fraction
156 * approximation ending either at exact match or maximum allowed
157 * nominator, denominator.
159 static void get_best_ratio(u32
*pnum
, u32
*pdenom
, u32 max_n
, u32 max_d
)
172 n
[i
] += (n
[i
^ 1] * whole
);
173 d
[i
] += (d
[i
^ 1] * whole
);
174 if ((n
[i
] > max_n
) || (d
[i
] > max_d
)) {
186 static int mixel_dphy_config_from_opts(struct phy
*phy
,
187 struct phy_configure_opts_mipi_dphy
*dphy_opts
,
188 struct mixel_dphy_cfg
*cfg
)
190 struct mixel_dphy_priv
*priv
= dev_get_drvdata(phy
->dev
.parent
);
191 unsigned long ref_clk
= clk_get_rate(priv
->phy_ref_clk
);
192 u32 lp_t
, numerator
, denominator
;
193 unsigned long long tmp
;
197 if (dphy_opts
->hs_clk_rate
> DATA_RATE_MAX_SPEED
||
198 dphy_opts
->hs_clk_rate
< DATA_RATE_MIN_SPEED
)
201 numerator
= dphy_opts
->hs_clk_rate
;
202 denominator
= ref_clk
;
203 get_best_ratio(&numerator
, &denominator
, 255, 256);
204 if (!numerator
|| !denominator
) {
205 dev_err(&phy
->dev
, "Invalid %d/%d for %ld/%ld\n",
206 numerator
, denominator
,
207 dphy_opts
->hs_clk_rate
, ref_clk
);
211 while ((numerator
< 16) && (denominator
<= 128)) {
216 * CM ranges between 16 and 255
217 * CN ranges between 1 and 32
218 * CO is power of 2: 1, 2, 4, 8
220 i
= __ffs(denominator
);
223 cfg
->cn
= denominator
>> i
;
227 if (cfg
->cm
< 16 || cfg
->cm
> 255 ||
228 cfg
->cn
< 1 || cfg
->cn
> 32 ||
229 cfg
->co
< 1 || cfg
->co
> 8) {
230 dev_err(&phy
->dev
, "Invalid CM/CN/CO values: %u/%u/%u\n",
231 cfg
->cm
, cfg
->cn
, cfg
->co
);
232 dev_err(&phy
->dev
, "for hs_clk/ref_clk=%ld/%ld ~ %d/%d\n",
233 dphy_opts
->hs_clk_rate
, ref_clk
,
234 numerator
, denominator
);
238 dev_dbg(&phy
->dev
, "hs_clk/ref_clk=%ld/%ld ~ %d/%d\n",
239 dphy_opts
->hs_clk_rate
, ref_clk
, numerator
, denominator
);
241 /* LP clock period */
242 tmp
= 1000000000000LL;
243 do_div(tmp
, dphy_opts
->lp_clk_rate
); /* ps */
248 dev_dbg(&phy
->dev
, "LP clock %lu, period: %u ps\n",
249 dphy_opts
->lp_clk_rate
, lp_t
);
251 /* hs_prepare: in lp clock periods */
252 if (2 * dphy_opts
->hs_prepare
> 5 * lp_t
) {
254 "hs_prepare (%u) > 2.5 * lp clock period (%u)\n",
255 dphy_opts
->hs_prepare
, lp_t
);
258 /* 00: lp_t, 01: 1.5 * lp_t, 10: 2 * lp_t, 11: 2.5 * lp_t */
259 if (dphy_opts
->hs_prepare
< lp_t
) {
262 tmp
= 2 * (dphy_opts
->hs_prepare
- lp_t
);
266 cfg
->m_prg_hs_prepare
= n
;
268 /* clk_prepare: in lp clock periods */
269 if (2 * dphy_opts
->clk_prepare
> 3 * lp_t
) {
271 "clk_prepare (%u) > 1.5 * lp clock period (%u)\n",
272 dphy_opts
->clk_prepare
, lp_t
);
275 /* 00: lp_t, 01: 1.5 * lp_t */
276 cfg
->mc_prg_hs_prepare
= dphy_opts
->clk_prepare
> lp_t
? 1 : 0;
278 /* hs_zero: formula from NXP BSP */
279 n
= (144 * (dphy_opts
->hs_clk_rate
/ 1000000) - 47500) / 10000;
280 cfg
->m_prg_hs_zero
= n
< 1 ? 1 : n
;
282 /* clk_zero: formula from NXP BSP */
283 n
= (34 * (dphy_opts
->hs_clk_rate
/ 1000000) - 2500) / 1000;
284 cfg
->mc_prg_hs_zero
= n
< 1 ? 1 : n
;
286 /* clk_trail, hs_trail: formula from NXP BSP */
287 n
= (103 * (dphy_opts
->hs_clk_rate
/ 1000000) + 10000) / 10000;
292 cfg
->m_prg_hs_trail
= n
;
293 cfg
->mc_prg_hs_trail
= n
;
295 /* rxhs_settle: formula from NXP BSP */
296 if (dphy_opts
->hs_clk_rate
< MBPS(80))
297 cfg
->rxhs_settle
= 0x0d;
298 else if (dphy_opts
->hs_clk_rate
< MBPS(90))
299 cfg
->rxhs_settle
= 0x0c;
300 else if (dphy_opts
->hs_clk_rate
< MBPS(125))
301 cfg
->rxhs_settle
= 0x0b;
302 else if (dphy_opts
->hs_clk_rate
< MBPS(150))
303 cfg
->rxhs_settle
= 0x0a;
304 else if (dphy_opts
->hs_clk_rate
< MBPS(225))
305 cfg
->rxhs_settle
= 0x09;
306 else if (dphy_opts
->hs_clk_rate
< MBPS(500))
307 cfg
->rxhs_settle
= 0x08;
309 cfg
->rxhs_settle
= 0x07;
311 dev_dbg(&phy
->dev
, "phy_config: %u %u %u %u %u %u %u\n",
312 cfg
->m_prg_hs_prepare
, cfg
->mc_prg_hs_prepare
,
313 cfg
->m_prg_hs_zero
, cfg
->mc_prg_hs_zero
,
314 cfg
->m_prg_hs_trail
, cfg
->mc_prg_hs_trail
,
320 static void mixel_phy_set_hs_timings(struct phy
*phy
)
322 struct mixel_dphy_priv
*priv
= phy_get_drvdata(phy
);
324 phy_write(phy
, priv
->cfg
.m_prg_hs_prepare
, DPHY_M_PRG_HS_PREPARE
);
325 phy_write(phy
, priv
->cfg
.mc_prg_hs_prepare
, DPHY_MC_PRG_HS_PREPARE
);
326 phy_write(phy
, priv
->cfg
.m_prg_hs_zero
, DPHY_M_PRG_HS_ZERO
);
327 phy_write(phy
, priv
->cfg
.mc_prg_hs_zero
, DPHY_MC_PRG_HS_ZERO
);
328 phy_write(phy
, priv
->cfg
.m_prg_hs_trail
, DPHY_M_PRG_HS_TRAIL
);
329 phy_write(phy
, priv
->cfg
.mc_prg_hs_trail
, DPHY_MC_PRG_HS_TRAIL
);
330 phy_write(phy
, priv
->cfg
.rxhs_settle
, priv
->devdata
->reg_rxhs_settle
);
333 static int mixel_dphy_set_pll_params(struct phy
*phy
)
335 struct mixel_dphy_priv
*priv
= dev_get_drvdata(phy
->dev
.parent
);
337 if (priv
->cfg
.cm
< 16 || priv
->cfg
.cm
> 255 ||
338 priv
->cfg
.cn
< 1 || priv
->cfg
.cn
> 32 ||
339 priv
->cfg
.co
< 1 || priv
->cfg
.co
> 8) {
340 dev_err(&phy
->dev
, "Invalid CM/CN/CO values! (%u/%u/%u)\n",
341 priv
->cfg
.cm
, priv
->cfg
.cn
, priv
->cfg
.co
);
344 dev_dbg(&phy
->dev
, "Using CM:%u CN:%u CO:%u\n",
345 priv
->cfg
.cm
, priv
->cfg
.cn
, priv
->cfg
.co
);
346 phy_write(phy
, CM(priv
->cfg
.cm
), DPHY_CM
);
347 phy_write(phy
, CN(priv
->cfg
.cn
), DPHY_CN
);
348 phy_write(phy
, CO(priv
->cfg
.co
), DPHY_CO
);
353 mixel_dphy_configure_mipi_dphy(struct phy
*phy
, union phy_configure_opts
*opts
)
355 struct mixel_dphy_priv
*priv
= phy_get_drvdata(phy
);
356 struct mixel_dphy_cfg cfg
= { 0 };
359 ret
= mixel_dphy_config_from_opts(phy
, &opts
->mipi_dphy
, &cfg
);
363 /* Update the configuration */
364 memcpy(&priv
->cfg
, &cfg
, sizeof(struct mixel_dphy_cfg
));
366 phy_write(phy
, 0x00, DPHY_LOCK_BYP
);
367 phy_write(phy
, 0x01, priv
->devdata
->reg_tx_rcal
);
368 phy_write(phy
, 0x00, priv
->devdata
->reg_auto_pd_en
);
369 phy_write(phy
, 0x02, priv
->devdata
->reg_rxlprp
);
370 phy_write(phy
, 0x02, priv
->devdata
->reg_rxcdrp
);
371 phy_write(phy
, 0x25, DPHY_TST
);
373 mixel_phy_set_hs_timings(phy
);
374 ret
= mixel_dphy_set_pll_params(phy
);
382 mixel_dphy_configure_lvds_phy(struct phy
*phy
, union phy_configure_opts
*opts
)
384 struct mixel_dphy_priv
*priv
= phy_get_drvdata(phy
);
385 struct phy_configure_opts_lvds
*lvds_opts
= &opts
->lvds
;
386 unsigned long data_rate
;
392 priv
->is_slave
= lvds_opts
->is_slave
;
394 /* LVDS interface pins */
395 regmap_write(priv
->lvds_regmap
, PHY_CTRL
,
396 CCM(CCM_1_2V
) | CA(CA_3_51MA
) | RFB
);
398 /* enable MODE8 only for slave LVDS PHY */
399 rsc
= priv
->id
? IMX_SC_R_MIPI_1
: IMX_SC_R_MIPI_0
;
400 ret
= imx_sc_misc_set_control(priv
->ipc_handle
, rsc
, IMX_SC_C_DUAL_MODE
,
401 lvds_opts
->is_slave
);
403 dev_err(&phy
->dev
, "Failed to configure MODE8: %d\n", ret
);
408 * Choose an appropriate divider ratio to meet the requirement of
409 * PLL VCO frequency range.
411 * ----- 640MHz ~ 1500MHz ------------ ---------------
412 * | VCO | ----------------> | CO divider | -> | LVDS data rate|
413 * ----- FVCO ------------ ---------------
414 * 1/2/4/8 div 7 * differential_clk_rate
416 data_rate
= 7 * lvds_opts
->differential_clk_rate
;
417 for (co
= 1; co
<= 8; co
*= 2) {
418 fvco
= data_rate
* co
;
420 if (fvco
>= MIN_VCO_FREQ
)
424 if (fvco
< MIN_VCO_FREQ
|| fvco
> MAX_VCO_FREQ
) {
425 dev_err(&phy
->dev
, "VCO frequency %lu is out of range\n", fvco
);
430 * CO is configurable, while CN and CM are not,
431 * as fixed ratios 1 and 7 are applied respectively.
433 phy_write(phy
, __ffs(co
), DPHY_CO
);
435 /* set reference clock rate */
436 clk_set_rate(priv
->phy_ref_clk
, lvds_opts
->differential_clk_rate
);
441 static int mixel_dphy_configure(struct phy
*phy
, union phy_configure_opts
*opts
)
444 dev_err(&phy
->dev
, "No configuration options\n");
448 if (phy
->attrs
.mode
== PHY_MODE_MIPI_DPHY
)
449 return mixel_dphy_configure_mipi_dphy(phy
, opts
);
450 else if (phy
->attrs
.mode
== PHY_MODE_LVDS
)
451 return mixel_dphy_configure_lvds_phy(phy
, opts
);
454 "Failed to configure PHY with invalid PHY mode: %d\n", phy
->attrs
.mode
);
460 mixel_dphy_validate_lvds_phy(struct phy
*phy
, union phy_configure_opts
*opts
)
462 struct phy_configure_opts_lvds
*lvds_cfg
= &opts
->lvds
;
464 if (lvds_cfg
->bits_per_lane_and_dclk_cycle
!= 7) {
465 dev_err(&phy
->dev
, "Invalid bits per LVDS data lane: %u\n",
466 lvds_cfg
->bits_per_lane_and_dclk_cycle
);
470 if (lvds_cfg
->lanes
!= 4) {
471 dev_err(&phy
->dev
, "Invalid LVDS data lanes: %u\n", lvds_cfg
->lanes
);
475 if (lvds_cfg
->differential_clk_rate
< MIN_LVDS_REFCLK_FREQ
||
476 lvds_cfg
->differential_clk_rate
> MAX_LVDS_REFCLK_FREQ
) {
478 "Invalid LVDS differential clock rate: %lu\n",
479 lvds_cfg
->differential_clk_rate
);
486 static int mixel_dphy_validate(struct phy
*phy
, enum phy_mode mode
, int submode
,
487 union phy_configure_opts
*opts
)
489 if (mode
== PHY_MODE_MIPI_DPHY
) {
490 struct mixel_dphy_cfg mipi_dphy_cfg
= { 0 };
492 return mixel_dphy_config_from_opts(phy
, &opts
->mipi_dphy
,
494 } else if (mode
== PHY_MODE_LVDS
) {
495 return mixel_dphy_validate_lvds_phy(phy
, opts
);
499 "Failed to validate PHY with invalid PHY mode: %d\n", mode
);
503 static int mixel_dphy_init(struct phy
*phy
)
505 phy_write(phy
, PWR_OFF
, DPHY_PD_PLL
);
506 phy_write(phy
, PWR_OFF
, DPHY_PD_DPHY
);
511 static int mixel_dphy_exit(struct phy
*phy
)
513 phy_write(phy
, 0, DPHY_CM
);
514 phy_write(phy
, 0, DPHY_CN
);
515 phy_write(phy
, 0, DPHY_CO
);
520 static int mixel_dphy_power_on_mipi_dphy(struct phy
*phy
)
522 struct mixel_dphy_priv
*priv
= phy_get_drvdata(phy
);
526 phy_write(phy
, PWR_ON
, DPHY_PD_PLL
);
527 ret
= regmap_read_poll_timeout(priv
->regmap
, DPHY_LOCK
, locked
,
528 locked
, PLL_LOCK_SLEEP
,
531 dev_err(&phy
->dev
, "Could not get DPHY lock (%d)!\n", ret
);
534 phy_write(phy
, PWR_ON
, DPHY_PD_DPHY
);
539 static int mixel_dphy_power_on_lvds_phy(struct phy
*phy
)
541 struct mixel_dphy_priv
*priv
= phy_get_drvdata(phy
);
545 regmap_update_bits(priv
->lvds_regmap
, PHY_CTRL
, LVDS_EN
, LVDS_EN
);
547 phy_write(phy
, PWR_ON
, DPHY_PD_DPHY
);
548 phy_write(phy
, PWR_ON
, DPHY_PD_PLL
);
550 /* do not wait for slave LVDS PHY being locked */
554 ret
= regmap_read_poll_timeout(priv
->regmap
, DPHY_LOCK
, locked
,
555 locked
, PLL_LOCK_SLEEP
,
558 dev_err(&phy
->dev
, "Could not get LVDS PHY lock (%d)!\n", ret
);
565 static int mixel_dphy_power_on(struct phy
*phy
)
567 struct mixel_dphy_priv
*priv
= phy_get_drvdata(phy
);
570 ret
= clk_prepare_enable(priv
->phy_ref_clk
);
574 if (phy
->attrs
.mode
== PHY_MODE_MIPI_DPHY
) {
575 ret
= mixel_dphy_power_on_mipi_dphy(phy
);
576 } else if (phy
->attrs
.mode
== PHY_MODE_LVDS
) {
577 ret
= mixel_dphy_power_on_lvds_phy(phy
);
580 "Failed to power on PHY with invalid PHY mode: %d\n",
590 clk_disable_unprepare(priv
->phy_ref_clk
);
594 static int mixel_dphy_power_off(struct phy
*phy
)
596 struct mixel_dphy_priv
*priv
= phy_get_drvdata(phy
);
598 phy_write(phy
, PWR_OFF
, DPHY_PD_PLL
);
599 phy_write(phy
, PWR_OFF
, DPHY_PD_DPHY
);
601 if (phy
->attrs
.mode
== PHY_MODE_LVDS
)
602 regmap_update_bits(priv
->lvds_regmap
, PHY_CTRL
, LVDS_EN
, 0);
604 clk_disable_unprepare(priv
->phy_ref_clk
);
609 static int mixel_dphy_set_mode(struct phy
*phy
, enum phy_mode mode
, int submode
)
611 struct mixel_dphy_priv
*priv
= phy_get_drvdata(phy
);
614 if (priv
->devdata
->is_combo
&& mode
!= PHY_MODE_LVDS
) {
615 dev_err(&phy
->dev
, "Failed to set PHY mode for combo PHY\n");
619 if (!priv
->devdata
->is_combo
&& mode
!= PHY_MODE_MIPI_DPHY
) {
620 dev_err(&phy
->dev
, "Failed to set PHY mode to MIPI DPHY\n");
624 if (priv
->devdata
->is_combo
) {
625 u32 rsc
= priv
->id
? IMX_SC_R_MIPI_1
: IMX_SC_R_MIPI_0
;
627 ret
= imx_sc_misc_set_control(priv
->ipc_handle
,
629 mode
== PHY_MODE_LVDS
);
632 "Failed to set PHY mode via SCU ipc: %d\n", ret
);
640 static const struct phy_ops mixel_dphy_phy_ops
= {
641 .init
= mixel_dphy_init
,
642 .exit
= mixel_dphy_exit
,
643 .power_on
= mixel_dphy_power_on
,
644 .power_off
= mixel_dphy_power_off
,
645 .set_mode
= mixel_dphy_set_mode
,
646 .configure
= mixel_dphy_configure
,
647 .validate
= mixel_dphy_validate
,
648 .owner
= THIS_MODULE
,
651 static const struct of_device_id mixel_dphy_of_match
[] = {
652 { .compatible
= "fsl,imx8mq-mipi-dphy",
653 .data
= &mixel_dphy_devdata
[MIXEL_IMX8MQ
] },
654 { .compatible
= "fsl,imx8qxp-mipi-dphy",
655 .data
= &mixel_dphy_devdata
[MIXEL_IMX8QXP
] },
658 MODULE_DEVICE_TABLE(of
, mixel_dphy_of_match
);
660 static int mixel_dphy_probe(struct platform_device
*pdev
)
662 struct device
*dev
= &pdev
->dev
;
663 struct device_node
*np
= dev
->of_node
;
664 struct phy_provider
*phy_provider
;
665 struct mixel_dphy_priv
*priv
;
673 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
677 priv
->devdata
= of_device_get_match_data(&pdev
->dev
);
681 base
= devm_platform_ioremap_resource(pdev
, 0);
683 return PTR_ERR(base
);
685 priv
->regmap
= devm_regmap_init_mmio(&pdev
->dev
, base
,
686 &mixel_dphy_regmap_config
);
687 if (IS_ERR(priv
->regmap
)) {
688 dev_err(dev
, "Couldn't create the DPHY regmap\n");
689 return PTR_ERR(priv
->regmap
);
692 priv
->phy_ref_clk
= devm_clk_get(&pdev
->dev
, "phy_ref");
693 if (IS_ERR(priv
->phy_ref_clk
)) {
694 dev_err(dev
, "No phy_ref clock found\n");
695 return PTR_ERR(priv
->phy_ref_clk
);
697 dev_dbg(dev
, "phy_ref clock rate: %lu\n",
698 clk_get_rate(priv
->phy_ref_clk
));
700 if (priv
->devdata
->is_combo
) {
702 syscon_regmap_lookup_by_phandle(np
, "fsl,syscon");
703 if (IS_ERR(priv
->lvds_regmap
)) {
704 ret
= PTR_ERR(priv
->lvds_regmap
);
705 dev_err_probe(dev
, ret
, "Failed to get LVDS regmap\n");
709 priv
->id
= of_alias_get_id(np
, "mipi-dphy");
711 dev_err(dev
, "Failed to get phy node alias id: %d\n",
716 ret
= imx_scu_get_handle(&priv
->ipc_handle
);
718 dev_err_probe(dev
, ret
,
719 "Failed to get SCU ipc handle\n");
724 dev_set_drvdata(dev
, priv
);
726 phy
= devm_phy_create(dev
, np
, &mixel_dphy_phy_ops
);
728 dev_err(dev
, "Failed to create phy %ld\n", PTR_ERR(phy
));
731 phy_set_drvdata(phy
, priv
);
733 phy_provider
= devm_of_phy_provider_register(dev
, of_phy_simple_xlate
);
735 return PTR_ERR_OR_ZERO(phy_provider
);
738 static struct platform_driver mixel_dphy_driver
= {
739 .probe
= mixel_dphy_probe
,
741 .name
= "mixel-mipi-dphy",
742 .of_match_table
= mixel_dphy_of_match
,
745 module_platform_driver(mixel_dphy_driver
);
747 MODULE_AUTHOR("NXP Semiconductor");
748 MODULE_DESCRIPTION("Mixel MIPI-DSI PHY driver");
749 MODULE_LICENSE("GPL");