On Thu, 04 Apr 2024, Yoshinori Sato wrote: > Various parameters of SM501 can be set using platform_data, > so parameters cannot be passed in the DeviceTree target. > Expands the parameters set in platform_data so that they can be > specified using DeviceTree properties. > > Signed-off-by: Yoshinori Sato <ysato@xxxxxxxxxxxxxxxxxxxx> > --- > drivers/mfd/sm501.c | 315 ++++++++++++++++++++++++++++++++++ > drivers/video/fbdev/sm501fb.c | 106 ++++++++++++ > 2 files changed, 421 insertions(+) I don't know exactly what this is, but I do know that I don't like it. If you manage to get it through another maintainer, more power to you, but it is not suitable for MFD. > diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c > index b3592982a83b..98a69e254f5f 100644 > --- a/drivers/mfd/sm501.c > +++ b/drivers/mfd/sm501.c > @@ -82,6 +82,16 @@ struct sm501_devdata { > unsigned int rev; > }; > > +struct sm501_config_props_uint { > + char *name; > + u32 shift; > +}; > + > +struct sm501_config_props_flag { > + char *clr_name; > + char *set_name; > + u32 bit; > +}; > > #define MHZ (1000 * 1000) > > @@ -1370,6 +1380,305 @@ static int sm501_init_dev(struct sm501_devdata *sm) > return 0; > } > > +static const struct sm501_config_props_uint misc_timing[] = { > + {"delay", 0}, > + {"-", 3}, > + {"divider", 4}, > + {"-", 6}, > + {"sm0", 8}, > + {"-", 12}, > + {"sm1", 16}, > + {"-", 20}, > + {"xc", 24}, > + {"-", 26}, > + {"ex", 28}, > + {NULL, 32}, > +}; > + > +static const struct sm501_config_props_flag misc_timing_flag[] = { > + {"usb-host-normal", "usb-host-simulation", 3}, > + {"no-acpi-control", "acpi-control", 6}, > + {"pll-debug-input", "pll-debug-output", 7}, > + {"sdram-clock-mode0-288mhz", "sdram-clock-mode0-div", 12}, > + {"sdram-clock-mode1-288mhz", "sdram-clock-mode1-div", 20}, > + {"usb-over-current-detect-disable", > + "usb-over-current-detect-enable", 23}, > + {}, > +}; > + > +static const struct sm501_config_props_uint misc_control[] = { > + {"hold", 18}, > + {"refresh", 21}, > + {"-", 23}, > + {"usbclk", 28}, > + {"pad", 30}, > + {NULL, 32}, > +}; > + > +static const struct sm501_config_props_flag misc_control_flag[] = { > + {"vr-mmio-30mb", "vr-mmio-62mb", 4}, > + {"usb-port-master", "usb-port-slave", 9}, > + {"burst-length-8", "burst-length-1", 10}, > + {"usb-slave-cpu", "usb-slave-8051", 11}, > + {"dac-power-enable", "dac-power-disable", 12}, > + {"pll-clock-count-disable", "pll-clock-count-enable", 15}, > + {"interrupt-normal", "interrupt-invarted", 16}, > + {"sh-ready-low", "sh-ready-high", 17}, > + {"xtal-freq-24mhz", "xtal-freq-12mhz", 24}, > + {"panel-data-18bit", "panel-dtat-24bit", 25}, > + {"latch-address-disable", "latch-address-enable", 26}, > + {"uart1", "ssp1", 27}, > + {}, > +}; > + > +/* Read configuration values */ > +static void sm501_of_read_config(struct device *dev, struct device_node *np, > + const char *prefix, > + const struct sm501_config_props_uint *props, > + const struct sm501_config_props_flag *props_flag, > + struct sm501_reg_init *ret) > +{ > + struct device_node *child; > + char *name; > + u32 shift; > + u32 width; > + u32 mask; > + u32 val; > + > + ret->mask = ~0; > + ret->set = 0; > + > + child = of_get_child_by_name(np, prefix); > + if (!child) > + return; > + > + while (props->name) { > + name = props->name; > + shift = props->shift; > + props++; > + > + if (name[0] == '-' || > + of_property_read_u32(child, name, &val)) > + continue; > + > + width = props->shift - shift; > + mask = (1 << width) - 1; > + if (mask < val) { > + dev_warn(dev, "%s invalid value %d", name, val); > + continue; > + } > + mask = ~(mask << shift); > + ret->mask &= mask; > + ret->set |= val << shift; > + } > + while (props_flag->clr_name) { > + val = ~0; > + if (of_property_read_bool(child, props_flag->clr_name)) > + val = 0; > + else if (of_property_read_bool(child, props_flag->set_name)) > + val = 1; > + if (val != ~0) { > + val <<= (props_flag->bit & 31); > + mask = 1 << (props_flag->bit & 31); > + ret->mask &= ~mask; > + ret->set |= val; > + } > + props_flag++; > + } > +} > + > +/* Read GPIO control */ > +/* > + * DT example. > + * gpio-pin-control { > + * pin@0 { > + * gpio-port; > + * }; > + * pin@1 { > + * function; > + * }; > + * }; > + */ > +static void sm501_of_read_gpio(struct device *dev, struct device_node *np, > + struct sm501_reg_init *hi, struct sm501_reg_init *low) > +{ > + struct device_node *gpio_group, *pin; > + const char *prop_mode; > + unsigned int pin_no; > + int mode; > + u64 mask; > + u64 set; > + > + mask = ~0; > + set = 0; > + gpio_group = of_get_child_by_name(np, "gpio-pin-control"); > + if (gpio_group) { > + for_each_child_of_node(gpio_group, pin) { > + mode = -1; > + if (sscanf(pin->full_name, "pin@%u", &pin_no) == 1) { > + if (of_property_read_bool(pin, "gpio-port")) > + mode = 0; > + else if (of_property_read_bool(pin, "function")) > + mode = 1; > + } > + /* GPIO0 - 47 and 55 -63 */ > + if (mode < 0 || > + (pin_no >= 64 || (pin_no >= 48 && pin_no <= 54))) { > + dev_warn(dev, > + "%s mode %s is invalid.", pin->name, prop_mode); > + } else { > + mask &= ~(1 << pin_no); > + set |= mode << pin_no; > + } > + } > + } > + hi->set = set >> 32; > + low->set = set & 0xffffffff; > + hi->mask = mask >> 32; > + low->mask = mask & 0xffffffff; > +} > + > +static inline int read_i2c_prop(struct device *dev, struct device_node *child, > + const char *name, u32 *val) > +{ > + if (of_property_read_u32(child, name, val)) { > + dev_warn(dev, "%s/%s not found. skip it.", of_node_full_name(child), name); > + return -ENOENT; > + } > + return 0; > +} > + > +/* Read GPIO I2C configuration */ > +/* > + * DT example. > + * gpio-i2c { > + * i2c@0 { > + * sda = <gpio-pin>; > + * scl = <gpio-pin>; > + * delay = <delay>; > + * timeout = <timeout>; > + * }; > + * i2c@1 { > + * : > + * }; > + * : > + * }; > + */ > +static int sm501_parse_dt_gpio_i2c(struct device *dev, struct sm501_platdata *plat, > + struct device_node *np) > +{ > + struct device_node *i2c_group, *child; > + unsigned int i; > + u32 i2c_nr; > + int err; > + > + i2c_group = of_get_child_by_name(np, "gpio-i2c"); > + if (!i2c_group) > + return 0; > + > + i2c_nr = of_get_child_count(i2c_group); > + plat->gpio_i2c = devm_kzalloc(dev, sizeof(*plat->gpio_i2c) * i2c_nr, > + GFP_KERNEL); > + if (!plat->gpio_i2c) > + return -ENOMEM; > + > + plat->gpio_i2c_nr = i2c_nr; > + i = 0; > + for_each_child_of_node(i2c_group, child) { > + u32 bus; > + > + if (sscanf(child->full_name, "i2c@%u", &bus) != 1) { > + dev_warn(dev, "Unknown address %s\n", child->name); > + continue; > + } > + > + err = 0; > + plat->gpio_i2c[i].bus_num = bus; > + err += read_i2c_prop(dev, child, "sda", &plat->gpio_i2c[i].pin_sda); > + err += read_i2c_prop(dev, child, "scl", &plat->gpio_i2c[i].pin_scl); > + err += read_i2c_prop(dev, child, "delay", &plat->gpio_i2c[i].udelay); > + err += read_i2c_prop(dev, child, "timeout", &plat->gpio_i2c[i].timeout); > + if (err == 0) > + i++; > + } > + > + return 0; > +} > + > +/* Read device functions */ > +static u32 sm501_read_devices(struct device *dev, struct device_node *np) > +{ > + static const char * const funcname[] = { > + "usb-host", "usb-slave", "ssp0", "ssp1", > + "uart0", "uart1", "fbaccel", "ac97", > + "i2s", "gpio", > + }; > + struct property *prop; > + unsigned int i; > + const char *s; > + u32 ret = 0; > + > + of_property_for_each_string(np, "smi,devices", prop, s) { > + for (i = 0; i < ARRAY_SIZE(funcname); i++) { > + if (strcmp(s, funcname[i]) == 0) { > + ret |= 1 << i; > + goto next; > + } > + } > + dev_warn(dev, "Unknown device function '%s'", s); > +next: > + } > + if (!ret) > + dev_warn(dev, "devices not defined. disable all functions."); > + return ret; > +} > + > +/* Build platform_data from OF property */ > +struct plat_dt { > + struct sm501_platdata plat; > + struct sm501_initdata init; > +}; > + > +static int sm501_parse_dt(struct sm501_devdata *sm, struct device_node *np) > +{ > + struct sm501_platdata *plat; > + struct plat_dt *dt_p; > + u32 word; > + int ret; > + > + dt_p = devm_kzalloc(sm->dev, sizeof(*dt_p), GFP_KERNEL); > + if (!dt_p) > + return -ENOMEM; > + > + plat = &dt_p->plat; > + plat->init = &dt_p->init; > + > + plat->init->devices = sm501_read_devices(sm->dev, np); > + /* mclk and m1xclk are not u32, so convert between them using intermediate variables. */ > + of_property_read_u32(np, "smi,mclk", &word); > + plat->init->mclk = word; > + of_property_read_u32(np, "smi,m1xclk", &word); > + plat->init->m1xclk = word; > + > + sm501_of_read_config(sm->dev, np, "misc-timing", > + misc_timing, misc_timing_flag, > + &plat->init->misc_timing); > + sm501_of_read_config(sm->dev, np, "misc-control", > + misc_control, misc_control_flag, > + &plat->init->misc_control); > + sm501_of_read_gpio(sm->dev, np, > + &plat->init->gpio_high, &plat->init->gpio_low); > + > + if (IS_ENABLED(CONFIG_MFD_SM501_GPIO) && > + (plat->init->devices & SM501_USE_GPIO)) { > + ret = sm501_parse_dt_gpio_i2c(sm->dev, plat, np); > + if (ret) > + return ret; > + } > + sm->platdata = plat; > + return 0; > +} > + > static int sm501_plat_probe(struct platform_device *dev) > { > struct sm501_devdata *sm; > @@ -1406,6 +1715,12 @@ static int sm501_plat_probe(struct platform_device *dev) > goto err_res; > } > > + if (IS_ENABLED(CONFIG_OF) && dev->dev.of_node) { > + ret = sm501_parse_dt(sm, dev->dev.of_node); > + if (ret) > + goto err_res; > + } > + > platform_set_drvdata(dev, sm); > > sm->regs = ioremap(sm->io_res->start, resource_size(sm->io_res)); > diff --git a/drivers/video/fbdev/sm501fb.c b/drivers/video/fbdev/sm501fb.c > index d6fdc1737cd2..5de00f2570aa 100644 > --- a/drivers/video/fbdev/sm501fb.c > +++ b/drivers/video/fbdev/sm501fb.c > @@ -1932,6 +1932,106 @@ static int sm501fb_start_one(struct sm501fb_info *info, > return 0; > } > > +#if defined(CONFIG_OF) > +static u32 read_display_flags(struct device_node *np) > +{ > + static const char * const name[] = { > + "use-init-done", "disable-at-exit", "use-hwcursor", "use-hwaccel", > + "panel-no-fpen", "panel-no-vbiasen", "panel-inv-fpen", "panel-inv-vbiasen", > + }; > + > + struct property *prop; > + unsigned int i; > + const char *s; > + u32 ret = 0; > + > + of_property_for_each_string(np, "smi,flags", prop, s) { > + for (i = 0; i < ARRAY_SIZE(name); i++) { > + if (strcmp(s, name[i]) == 0) { > + ret |= 1 << i; > + break; > + } > + } > + } > + return ret; > +} > + > +/* parse CRT / panel configuration */ > +static struct sm501_platdata_fbsub *dt_fbsub(struct device *dev, > + struct device_node *np, > + const char *name) > +{ > + struct sm501_platdata_fbsub *fbsub = NULL; > + struct fb_videomode *def_mode = NULL; > + struct device_node *child; > + const void *p_edid; > + u32 flags = 0; > + u32 bpp = 0; > + int len; > + > + child = of_get_child_by_name(np, name); > + if (child == NULL) > + return NULL; > + > + p_edid = of_get_property(child, "edid", &len); > + if (p_edid && len == EDID_LENGTH) { > + struct fb_monspecs *specs; > + u8 *edid; > + > + edid = kmemdup(p_edid, EDID_LENGTH, GFP_KERNEL); > + if (edid) { > + specs = kzalloc(sizeof(*specs), GFP_KERNEL); > + if (specs) { > + fb_edid_to_monspecs(edid, specs); > + def_mode = specs->modedb; > + } > + } > + kfree(edid); > + } > + > + of_property_read_u32(child, "bpp", &bpp); > + > + /* If flags property is obtained, fbsub is returned. */ > + flags = read_display_flags(child); > + if (flags) { > + fbsub = devm_kzalloc(dev, sizeof(*fbsub), GFP_KERNEL); > + if (fbsub) { > + fbsub->def_mode = def_mode; > + fbsub->def_bpp = bpp; > + fbsub->flags = flags; > + } > + } > + return fbsub; > +} > + > +/* Build platform_data from OF property */ > +static struct sm501_platdata_fb *pdata_from_dt(struct device *dev, struct device_node *np) > +{ > + enum sm501_fb_routing fb_route = SM501_FB_OWN; > + struct sm501_platdata_fb *pdata = NULL; > + struct sm501_platdata_fbsub *fb_crt; > + struct sm501_platdata_fbsub *fb_pnl; > + unsigned int flags = 0; > + > + if (of_property_read_bool(np, "route-crt-panel")) > + fb_route = SM501_FB_CRT_PANEL; > + if (of_property_read_bool(np, "swap-fb-endian")) > + flags = SM501_FBPD_SWAP_FB_ENDIAN; > + fb_crt = dt_fbsub(dev, np, "crt"); > + fb_pnl = dt_fbsub(dev, np, "panel"); > + if (fb_crt || fb_pnl) { > + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); > + if (pdata) { > + pdata->fb_route = fb_route; > + pdata->flags = flags; > + pdata->fb_crt = fb_crt; > + pdata->fb_pnl = fb_pnl; > + } > + } > + return pdata; > +} > +#endif > + > static int sm501fb_probe(struct platform_device *pdev) > { > struct sm501fb_info *info; > @@ -1974,6 +2074,12 @@ static int sm501fb_probe(struct platform_device *pdev) > if (info->edid_data) > found = 1; > } > + /* Get platform data compatible configuration */ > + if (!found) { > + info->pdata = pdata_from_dt(dev, np); > + if (info->pdata) > + found = 1; > + } > } > #endif > if (!found) { > -- > 2.39.2 > -- Lee Jones [李琼斯]