/* SPDX-License-Identifier: GPL-2.0+ */ /* * AMD ISP Pinctrl Driver * * Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved. * */ #include #include #include #include "pinctrl-amdisp.h" #define DRV_NAME "amdisp-pinctrl" #define GPIO_CONTROL_PIN 4 #define GPIO_OFFSET_0 0x0 #define GPIO_OFFSET_1 0x4 #define GPIO_OFFSET_2 0x50 static const u32 gpio_offset[] = { GPIO_OFFSET_0, GPIO_OFFSET_1, GPIO_OFFSET_2 }; struct amdisp_pinctrl_data { const struct pinctrl_pin_desc *pins; unsigned int npins; const struct amdisp_function *functions; unsigned int nfunctions; const struct amdisp_pingroup *groups; unsigned int ngroups; }; static const struct amdisp_pinctrl_data amdisp_pinctrl_data = { .pins = amdisp_pins, .npins = ARRAY_SIZE(amdisp_pins), .functions = amdisp_functions, .nfunctions = ARRAY_SIZE(amdisp_functions), .groups = amdisp_groups, .ngroups = ARRAY_SIZE(amdisp_groups), }; struct amdisp_pinctrl { struct device *dev; struct pinctrl_dev *pctrl; struct pinctrl_desc desc; struct pinctrl_gpio_range gpio_range; struct gpio_chip gc; const struct amdisp_pinctrl_data *data; void __iomem *gpiobase; raw_spinlock_t lock; }; static int amdisp_get_groups_count(struct pinctrl_dev *pctldev) { struct amdisp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); return pctrl->data->ngroups; } static const char *amdisp_get_group_name(struct pinctrl_dev *pctldev, unsigned int group) { struct amdisp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); return pctrl->data->groups[group].name; } static int amdisp_get_group_pins(struct pinctrl_dev *pctldev, unsigned int group, const unsigned int **pins, unsigned int *num_pins) { struct amdisp_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); *pins = pctrl->data->groups[group].pins; *num_pins = pctrl->data->groups[group].npins; return 0; } const struct pinctrl_ops amdisp_pinctrl_ops = { .get_groups_count = amdisp_get_groups_count, .get_group_name = amdisp_get_group_name, .get_group_pins = amdisp_get_group_pins, }; static int amdisp_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) { /* amdisp gpio only has output mode */ return GPIO_LINE_DIRECTION_OUT; } static int amdisp_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio) { return -EOPNOTSUPP; } static int amdisp_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio, int value) { /* Nothing to do, amdisp gpio only has output mode */ return 0; } static int amdisp_gpio_get(struct gpio_chip *gc, unsigned int gpio) { unsigned long flags; u32 pin_reg; struct amdisp_pinctrl *pctrl = gpiochip_get_data(gc); raw_spin_lock_irqsave(&pctrl->lock, flags); pin_reg = readl(pctrl->gpiobase + gpio_offset[gpio]); raw_spin_unlock_irqrestore(&pctrl->lock, flags); return !!(pin_reg & BIT(GPIO_CONTROL_PIN)); } static void amdisp_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) { unsigned long flags; u32 pin_reg; struct amdisp_pinctrl *pctrl = gpiochip_get_data(gc); raw_spin_lock_irqsave(&pctrl->lock, flags); pin_reg = readl(pctrl->gpiobase + gpio_offset[gpio]); if (value) pin_reg |= BIT(GPIO_CONTROL_PIN); else pin_reg &= ~BIT(GPIO_CONTROL_PIN); writel(pin_reg, pctrl->gpiobase + gpio_offset[gpio]); raw_spin_unlock_irqrestore(&pctrl->lock, flags); } static int amdisp_gpiochip_add(struct platform_device *pdev, struct amdisp_pinctrl *pctrl) { struct gpio_chip *gc = &pctrl->gc; struct pinctrl_gpio_range *grange = &pctrl->gpio_range; int ret; gc->label = dev_name(pctrl->dev); gc->parent = &pdev->dev; gc->names = amdisp_range_pins_name; gc->request = gpiochip_generic_request; gc->free = gpiochip_generic_free; gc->get_direction = amdisp_gpio_get_direction; gc->direction_input = amdisp_gpio_direction_input; gc->direction_output = amdisp_gpio_direction_output; gc->get = amdisp_gpio_get; gc->set = amdisp_gpio_set; gc->base = -1; gc->ngpio = ARRAY_SIZE(amdisp_range_pins); grange->id = 0; grange->pin_base = 0; grange->base = 0; grange->pins = amdisp_range_pins; grange->npins = ARRAY_SIZE(amdisp_range_pins); grange->name = gc->label; grange->gc = gc; ret = devm_gpiochip_add_data(&pdev->dev, gc, pctrl); if (ret) return ret; pinctrl_add_gpio_range(pctrl->pctrl, grange); return 0; } static int amdisp_pinctrl_probe(struct platform_device *pdev) { struct amdisp_pinctrl *pctrl; struct resource *res; int ret; pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); if (!pctrl) return -ENOMEM; pdev->dev.init_name = DRV_NAME; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -EINVAL; pctrl->gpiobase = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(pctrl->gpiobase)) return PTR_ERR(pctrl->gpiobase); platform_set_drvdata(pdev, pctrl); pctrl->dev = &pdev->dev; pctrl->data = &amdisp_pinctrl_data; pctrl->desc.owner = THIS_MODULE; pctrl->desc.pctlops = &amdisp_pinctrl_ops; pctrl->desc.pmxops = NULL; pctrl->desc.name = dev_name(&pdev->dev); pctrl->desc.pins = pctrl->data->pins; pctrl->desc.npins = pctrl->data->npins; ret = devm_pinctrl_register_and_init(&pdev->dev, &pctrl->desc, pctrl, &pctrl->pctrl); if (ret) return ret; ret = pinctrl_enable(pctrl->pctrl); if (ret) return ret; ret = amdisp_gpiochip_add(pdev, pctrl); if (ret) return ret; return 0; } static struct platform_driver amdisp_pinctrl_driver = { .driver = { .name = DRV_NAME, }, .probe = amdisp_pinctrl_probe, }; module_platform_driver(amdisp_pinctrl_driver); MODULE_AUTHOR("Benjamin Chan "); MODULE_AUTHOR("Pratap Nirujogi "); MODULE_DESCRIPTION("AMDISP pinctrl driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" DRV_NAME);