// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright(c) 2026 Realtek Corporation
 */

#include "core.h"
#include "debug.h"
#include "led.h"
#include "reg.h"

static const struct rtw89_led_gpio_entry rtw89_common_led_gpios[] = {{
	.pin = 8,
	.pinmux = {.addr = R_AX_GPIO8_15_FUNC_SEL, .mask = GENMASK(3, 0), .data = 0xf},
	.mode = {.addr = R_AX_GPIO_EXT_CTRL + 2, .data = BIT(0) | BIT(8)},
	.out = {.addr = R_AX_GPIO_EXT_CTRL + 1, .data = BIT(0)},
}};

static const struct rtw89_led_desc rtw89_common_led_desc = {
	.gpios = rtw89_common_led_gpios,
	.n_gpio = ARRAY_SIZE(rtw89_common_led_gpios),
};

void rtw89_led_gpio_config(struct rtw89_dev *rtwdev,
			   const struct rtw89_led_gpio_entry *e)
{
	rtw89_write32_mask(rtwdev, e->pinmux.addr, e->pinmux.mask, e->pinmux.data);
	rtw89_write16_clr(rtwdev, e->mode.addr, e->mode.data);
}

void rtw89_led_gpio_set(struct rtw89_dev *rtwdev,
			const struct rtw89_led_gpio_entry *e,
			enum led_brightness brightness)
{
	if (brightness == LED_OFF) {
		rtw89_write16_clr(rtwdev, e->mode.addr, e->mode.data);
	} else {
		rtw89_write16_set(rtwdev, e->mode.addr, e->mode.data);
		rtw89_write8_clr(rtwdev, e->out.addr, e->out.data);
	}
}

static int rtw89_led_set(struct led_classdev *led, enum led_brightness brightness)
{
	struct rtw89_led *rtw_led = container_of(led, struct rtw89_led, led);
	struct rtw89_dev *rtwdev = container_of(rtw_led, struct rtw89_dev, led);
	const struct rtw89_led_desc *desc = rtw_led->desc;
	const struct rtw89_led_gpio_entry *e = &desc->gpios[0];

	if (rtw_led->brightness_cache[0] == brightness) {
		rtw89_debug(rtwdev, RTW89_DBG_LED, "led_set: pin=%u skip (no change)\n",
			    e->pin);
		return 0;
	}

	wiphy_lock(rtwdev->hw->wiphy);

	rtw89_debug(rtwdev, RTW89_DBG_LED, "led_set: pin=%u brightness=%u\n",
		    e->pin, brightness);
	rtw89_led_gpio_set(rtwdev, e, brightness);
	rtw_led->brightness_cache[0] = brightness;

	wiphy_unlock(rtwdev->hw->wiphy);

	return 0;
}

static int rtw89_led_sc_init(struct rtw89_dev *rtwdev, const struct rtw89_led_desc *desc)
{
	struct rtw89_led *rtw_led = &rtwdev->led;
	int ret;

	snprintf(rtw_led->name, sizeof(rtw_led->name), "rtw89-%s",
		 wiphy_name(rtwdev->hw->wiphy));
	rtw_led->led.name = rtw_led->name;
	rtw_led->led.brightness_set_blocking = rtw89_led_set;
	rtw_led->led.max_brightness = LED_ON;
	rtw_led->led.default_trigger = ieee80211_get_assoc_led_name(rtwdev->hw);

	ret = led_classdev_register(rtwdev->dev, &rtw_led->led);
	if (ret) {
		rtw89_warn(rtwdev, "failed to register LED, ret=%d\n", ret);
		return ret;
	}

	rtw89_led_gpio_config(rtwdev, &desc->gpios[0]);

	return 0;
}

static void rtw89_led_sc_deinit(struct rtw89_dev *rtwdev)
{
	struct rtw89_led *rtw_led = &rtwdev->led;

	led_classdev_unregister(&rtw_led->led);
}

void rtw89_led_init(struct rtw89_dev *rtwdev)
{
	const struct rtw89_led_desc *desc = &rtw89_common_led_desc;
	struct rtw89_led *rtw_led = &rtwdev->led;
	const struct rtw89_board_variant *board = rtwdev->board;
	int ret;
	int i;

	/* single-GPIO monochrome LED is the only supported layout */
	BUILD_BUG_ON(ARRAY_SIZE(rtw89_common_led_gpios) != 1);

	if (board)
		desc = board->led_desc;
	if (!desc->n_gpio || desc->n_gpio > RTW89_LED_MAX_NUM)
		return;

	rtw_led->desc = desc;
	for (i = 0; i < ARRAY_SIZE(rtw_led->brightness_cache); i++)
		rtw_led->brightness_cache[i] = LED_OFF;

	if (desc->n_gpio == 1)
		ret = rtw89_led_sc_init(rtwdev, desc);
	else
		ret = rtw89_led_mc_init(rtwdev, desc);

	if (ret)
		return;

	rtw_led->registered = true;
}

void rtw89_led_deinit(struct rtw89_dev *rtwdev)
{
	struct rtw89_led *rtw_led = &rtwdev->led;
	const struct rtw89_led_desc *desc = rtw_led->desc;

	if (!rtw_led->registered)
		return;

	if (desc->n_gpio == 1)
		rtw89_led_sc_deinit(rtwdev);
	else
		rtw89_led_mc_deinit(rtwdev);
}