From: Alexey Sheplyakov <asheplyakov@basealt.ru> To: devel-kernel@lists.altlinux.org Cc: "Роман Ставцев" <rst@basealt.ru>, "Игорь Чудов" <nir@basealt.ru>, "Евгений Синельников" <sin@basealt.ru>, "Дмитрий Терёхин" <jqt4@basealt.ru> Subject: [d-kernel] [PATCH 19/32] drm: new bridge driver - stdp4028 Date: Wed, 14 Dec 2022 17:19:06 +0400 Message-ID: <20221214131919.681481-19-asheplyakov@basealt.ru> (raw) In-Reply-To: <20221214131919.681481-1-asheplyakov@basealt.ru> From: "Vadim V. Vlasov" <vvv19xx@gmail.com> MegaChips stdp4028 is LVDS to DP bridge. The driver can work in interrupt or poll mode. Videomodes may be specified in the devicetree or read from EDID. Co-developed-by: Vadim V. Vlasov <vvv19xx@gmail.com> Signed-off-by: Alexey Sheplyakov <asheplyakov@basealt.ru> X-feature-Baikal-M --- drivers/gpu/drm/bridge/Kconfig | 8 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/stdp4028.c | 484 ++++++++++++++++++++++++++++++ 3 files changed, 493 insertions(+) create mode 100644 drivers/gpu/drm/bridge/stdp4028.c diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 57946d80b02d..b6b83c4e9083 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -379,6 +379,14 @@ config DRM_TI_TPD12S015 Texas Instruments TPD12S015 HDMI level shifter and ESD protection driver. +config DRM_STDP4028 + tristate "MegaChips STDP4028 DP bridge" + depends on OF + select DRM_KMS_HELPER + select DRM_PANEL + help + MegaChips STDP4028 DP bridge driver + source "drivers/gpu/drm/bridge/analogix/Kconfig" source "drivers/gpu/drm/bridge/adv7511/Kconfig" diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 1884803c6860..e8312848293f 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o obj-$(CONFIG_DRM_TI_TPD12S015) += ti-tpd12s015.o obj-$(CONFIG_DRM_NWL_MIPI_DSI) += nwl-dsi.o obj-$(CONFIG_DRM_ITE_IT66121) += ite-it66121.o +obj-$(CONFIG_DRM_STDP4028) += stdp4028.o obj-y += analogix/ obj-y += cadence/ diff --git a/drivers/gpu/drm/bridge/stdp4028.c b/drivers/gpu/drm/bridge/stdp4028.c new file mode 100644 index 000000000000..5a27db85ad4b --- /dev/null +++ b/drivers/gpu/drm/bridge/stdp4028.c @@ -0,0 +1,484 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for MegaChips STDP4028 LVDS to DP display bridge + */ + +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> + +/* video modes */ +#include <video/display_timing.h> +#include <video/of_display_timing.h> +#include <video/videomode.h> + +#define MAX_PIXEL_CLOCK 330000 + +#define EDID_EXT_BLOCK_CNT 0x7E + +#define STDP4028_PRODUCT_ID_REG 0x00 +#define STDP4028_IRQ_OUT_CONF_REG 0x02 +#define STDP4028_IRQ_STS_REG 0x03 +#define STDP4028_I2C_CTRL_REG 0x08 +#define STDP4028_LVDS_FMT_REG 0x0B +#define STDP4028_LVDS_CTRL0_REG 0x0C +#define STDP4028_DPTX_IRQ_EN_REG 0x3C +#define STDP4028_DPTX_IRQ_STS_REG 0x3D +#define STDP4028_DPTX_STS_REG 0x3E + +#define STDP4028_DPTX_DP_IRQ_EN 0x10 + +#define STDP4028_DPTX_HOTPLUG_IRQ_EN 0x04 +#define STDP4028_DPTX_LINK_CH_IRQ_EN 0x20 +#define STDP4028_DPTX_IRQ_CONFIG \ + (STDP4028_DPTX_LINK_CH_IRQ_EN | STDP4028_DPTX_HOTPLUG_IRQ_EN) + +#define STDP4028_DPTX_HOTPLUG_STS 0x02 +#define STDP4028_DPTX_LINK_STS 0x10 +#define STDP4028_CON_STATE_CONNECTED \ + (STDP4028_DPTX_HOTPLUG_STS | STDP4028_DPTX_LINK_STS) + +#define STDP4028_DPTX_HOTPLUG_CH_STS 0x04 +#define STDP4028_DPTX_LINK_CH_STS 0x20 +#define STDP4028_DPTX_IRQ_CLEAR \ + (STDP4028_DPTX_LINK_CH_STS | STDP4028_DPTX_HOTPLUG_CH_STS) + +struct stdp4028 { + struct drm_connector connector; + struct drm_bridge bridge; + struct i2c_client *stdp4028_i2c; + struct i2c_client *edid_i2c; + struct edid *edid; + struct gpio_desc *reset_gpio; + struct mutex lock; + int channels; + int chan_cfg; +}; + +static inline int stdp_read(struct stdp4028 *stdp, int reg) +{ + int ret; + + ret = i2c_smbus_read_word_data(stdp->stdp4028_i2c, reg); + if (ret < 0) + return ret; + return be16_to_cpu(ret); +} + +static inline int stdp_write(struct stdp4028 *stdp, int reg, u16 val) +{ + val = cpu_to_be16(val); + return i2c_smbus_write_word_data(stdp->stdp4028_i2c, reg, val); +} + +#define bridge_to_stdp4028(bridge) \ + container_of(bridge, struct stdp4028, bridge) + +#define connector_to_stdp4028(connector) \ + container_of(connector, struct stdp4028, connector) + +static u8 *stdp4028_get_edid(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + unsigned char start = 0x00; + unsigned int total_size; + u8 *block = kmalloc(EDID_LENGTH, GFP_KERNEL); + + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &start, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = EDID_LENGTH, + .buf = block, + } + }; + + if (!block) + return NULL; + + if (i2c_transfer(adapter, msgs, 2) != 2) { + DRM_ERROR("Unable to read EDID.\n"); + goto err; + } + + if (!drm_edid_block_valid(block, 0, false, NULL)) { + DRM_ERROR("Invalid EDID data\n"); + goto err; + } + + total_size = (block[EDID_EXT_BLOCK_CNT] + 1) * EDID_LENGTH; + if (total_size > EDID_LENGTH) { + kfree(block); + block = kmalloc(total_size, GFP_KERNEL); + if (!block) + return NULL; + + /* Yes, read the entire buffer, and do not skip the first + * EDID_LENGTH bytes. + */ + start = 0x00; + msgs[1].len = total_size; + msgs[1].buf = block; + + if (i2c_transfer(adapter, msgs, 2) != 2) { + DRM_ERROR("Unable to read EDID extension blocks.\n"); + goto err; + } + } + + return block; + +err: + kfree(block); + return NULL; +} + +/* + * Get videomode specified in the devicetree. + * Return 1 on success, 0 otherwise. + */ +static int stdp4028_get_of_modes(struct drm_connector *connector) +{ + struct stdp4028 *stdp = connector_to_stdp4028(connector); + struct i2c_client *client = stdp->stdp4028_i2c; + struct drm_display_mode *mode; + struct device_node *np = client->dev.of_node; + struct display_timing timing; + struct videomode video_mode; + int ret; + + ret = of_get_display_timing(np, "panel-timing", &timing); + if (ret < 0) + return 0; + + videomode_from_timing(&timing, &video_mode); + + mode = drm_mode_create(connector->dev); + if (!mode) + return 0; + drm_display_mode_from_videomode(&video_mode, mode); + mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + + drm_mode_probed_add(connector, mode); + return 1; +} + +static int stdp4028_get_modes(struct drm_connector *connector) +{ + struct stdp4028 *stdp; + struct i2c_client *client; + int num_modes = 0; + + stdp = connector_to_stdp4028(connector); + client = stdp->edid_i2c; + + mutex_lock(&stdp->lock); + + num_modes = stdp4028_get_of_modes(connector); + if (num_modes > 0) { + mutex_unlock(&stdp->lock); + return num_modes; + } + + kfree(stdp->edid); + stdp->edid = (struct edid *) stdp4028_get_edid(client); + + if (stdp->edid) { + drm_connector_update_edid_property(connector, stdp->edid); + num_modes = drm_add_edid_modes(connector, stdp->edid); + } + + mutex_unlock(&stdp->lock); + + return num_modes; +} + + +static enum drm_mode_status stdp4028_mode_valid( + struct drm_connector *connector, struct drm_display_mode *mode) +{ + if (mode->clock > MAX_PIXEL_CLOCK) { + DRM_INFO("The pixel clock for the mode %s is too high, and not supported.", + mode->name); + return MODE_CLOCK_HIGH; + } + + return MODE_OK; +} + +static const struct +drm_connector_helper_funcs stdp4028_connector_helper_funcs = { + .get_modes = stdp4028_get_modes, + .mode_valid = stdp4028_mode_valid, +}; + +static enum drm_connector_status stdp4028_detect( + struct drm_connector *connector, bool force) +{ + struct stdp4028 *stdp = connector_to_stdp4028(connector); + s32 link_state; + + link_state = stdp_read(stdp, STDP4028_DPTX_STS_REG); + + if (link_state == STDP4028_CON_STATE_CONNECTED) + return connector_status_connected; + + if (link_state == 0) + return connector_status_disconnected; + + return connector_status_unknown; +} + +static const struct drm_connector_funcs stdp4028_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = stdp4028_detect, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static irqreturn_t stdp4028_irq_handler(int irq, void *dev_id) +{ + struct stdp4028 *stdp = dev_id; + + mutex_lock(&stdp->lock); + + stdp_write(stdp, STDP4028_DPTX_IRQ_STS_REG, STDP4028_DPTX_IRQ_CLEAR); + + mutex_unlock(&stdp->lock); + + if (stdp->connector.dev) + drm_kms_helper_hotplug_event(stdp->connector.dev); + + return IRQ_HANDLED; +} + +static int stdp4028_create_connector(struct drm_bridge *bridge) +{ + int ret; + struct stdp4028 *stdp + = bridge_to_stdp4028(bridge); + struct drm_connector *connector = &stdp->connector; + + if (!bridge->encoder) { + DRM_ERROR("Parent encoder object not found"); + return -ENODEV; + } + + if (stdp->stdp4028_i2c->irq) + connector->polled = DRM_CONNECTOR_POLL_HPD; + else + connector->polled = DRM_CONNECTOR_POLL_CONNECT | + DRM_CONNECTOR_POLL_DISCONNECT; + + drm_connector_helper_add(connector, &stdp4028_connector_helper_funcs); + + ret = drm_connector_init(bridge->dev, connector, + &stdp4028_connector_funcs, + DRM_MODE_CONNECTOR_DisplayPort); + if (ret) { + DRM_ERROR("Failed to initialize connector with drm\n"); + return ret; + } + + return drm_connector_attach_encoder(connector, bridge->encoder); +} + +static int stdp4028_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct stdp4028 *stdp + = bridge_to_stdp4028(bridge); + + /* Configures the bridge to re-enable interrupts after each ack. */ + stdp_write(stdp, STDP4028_IRQ_OUT_CONF_REG, STDP4028_DPTX_DP_IRQ_EN); + + /* Enable interrupts */ + stdp_write(stdp, STDP4028_DPTX_IRQ_EN_REG, STDP4028_DPTX_IRQ_CONFIG); + + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) + return 0; + + return stdp4028_create_connector(bridge); +} + +static const struct drm_bridge_funcs stdp4028_funcs = { + .attach = stdp4028_attach, +}; + +static int stdp4028_probe(struct i2c_client *stdp4028_i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &stdp4028_i2c->dev; + struct stdp4028 *bridge; + int ret; + u32 edid_i2c_reg, channels, chan_cfg; + enum of_gpio_flags flags; + int reset_gpio, i; + int reg; + + bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL); + if (!bridge) + return -ENOMEM; + + mutex_init(&bridge->lock); + + bridge->stdp4028_i2c = stdp4028_i2c; + bridge->bridge.driver_private = bridge; + i2c_set_clientdata(stdp4028_i2c, bridge); + + reset_gpio = of_get_named_gpio_flags(dev->of_node, + "reset-gpios", 0, &flags); + if (gpio_is_valid(reset_gpio)) { + unsigned long gpio_flags; + + /* + * We will set GPIO to "inactive" state instead of toggling + * reset. If the chip is not ready we will return -EPROBE_DEFER + * and retry later. + */ + if (!(flags & OF_GPIO_ACTIVE_LOW)) + gpio_flags = GPIOF_ACTIVE_LOW | GPIOF_OUT_INIT_LOW; + else + gpio_flags = GPIOF_OUT_INIT_HIGH; + ret = devm_gpio_request_one(dev, reset_gpio, gpio_flags, + "stdp-reset"); + if (ret) { + dev_err(dev, "request GPIO failed (%d)\n", ret); + /* continue anyway */ + } else { + bridge->reset_gpio = gpio_to_desc(reset_gpio); + udelay(100); + } + } else if (reset_gpio == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } + + ret = of_property_read_u32(dev->of_node, "channels", &channels); + if (ret) + channels = 1; + bridge->channels = channels; + + ret = of_property_read_u32(dev->of_node, "chan-cfg", &chan_cfg); + if (ret) + chan_cfg = 0; + bridge->chan_cfg = chan_cfg; + + ret = of_property_read_u32(dev->of_node, "edid-reg", &edid_i2c_reg); + if (ret) { + dev_warn(dev, "edid-reg not specified, assuming 0x50...\n"); + edid_i2c_reg = 0x50; + } + + /* Configure stdp registers */ + reg = stdp_read(bridge, STDP4028_PRODUCT_ID_REG); + if (reg < 0) { + dev_err(dev, "Can't read stdp id (%d)\n", reg); + return -EPROBE_DEFER; /* probably, reset not complete */ + } + + dev_info(dev, "stdp id word: %x\n", reg); + + for (i = 0; i < 10; i++) { + reg = stdp_read(bridge, STDP4028_IRQ_STS_REG); + if (reg > 0 && reg & 0x800) + break; + usleep_range(1000, 1500); + } + dev_dbg(dev, "STDP status word %x (i = %d)\n", reg, i); + stdp_write(bridge, STDP4028_IRQ_STS_REG, 0x800); //clear + /* enable edid addr */ + stdp_write(bridge, STDP4028_I2C_CTRL_REG, (edid_i2c_reg << 1) | 0x400); + + if (channels == 4) + reg = 2; + else if (channels == 2) + reg = 1; + else + reg = 0; + reg |= chan_cfg << 2; + stdp_write(bridge, STDP4028_LVDS_CTRL0_REG, reg); + + bridge->edid_i2c = i2c_new_dummy_device(stdp4028_i2c->adapter, edid_i2c_reg); + + if (!bridge->edid_i2c) + return -ENOMEM; + + bridge->bridge.funcs = &stdp4028_funcs; + bridge->bridge.of_node = dev->of_node; + drm_bridge_add(&bridge->bridge); + + /* Clear pending interrupts since power up. */ + stdp_write(bridge, STDP4028_DPTX_IRQ_STS_REG, STDP4028_DPTX_IRQ_CLEAR); + + if (stdp4028_i2c->irq) { + ret = devm_request_threaded_irq(&stdp4028_i2c->dev, + stdp4028_i2c->irq, NULL, + stdp4028_irq_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "stdp-lvds-dp", bridge); + if (ret) + return ret; + + /* enable DPTX IRQs */ + stdp_write(bridge, STDP4028_IRQ_OUT_CONF_REG, + STDP4028_DPTX_DP_IRQ_EN); + stdp_write(bridge, STDP4028_DPTX_IRQ_EN_REG, + STDP4028_DPTX_IRQ_CONFIG); + } + + return 0; +} + +static void stdp4028_remove(struct i2c_client *stdp4028_i2c) +{ + struct stdp4028 *stdp = i2c_get_clientdata(stdp4028_i2c); + + drm_bridge_remove(&stdp->bridge); + i2c_unregister_device(stdp->edid_i2c); + + kfree(stdp->edid); +} + +static const struct i2c_device_id stdp4028_i2c_table[] = { + {"stdp4028-lvds-dp", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, stdp4028_i2c_table); + +static const struct of_device_id stdp4028_match[] = { + { .compatible = "megachips,stdp4028-lvds-dp" }, + {}, +}; +MODULE_DEVICE_TABLE(of, stdp4028_match); + +static struct i2c_driver stdp4028_driver = { + .id_table = stdp4028_i2c_table, + .probe = stdp4028_probe, + .remove = stdp4028_remove, + .driver = { + .name = "stdp4028-lvds-dp", + .of_match_table = stdp4028_match, + }, +}; +module_i2c_driver(stdp4028_driver); + +MODULE_AUTHOR("Vadim V. Vlasov <vvv19xx at gmail.com>"); +MODULE_DESCRIPTION("STDP4028 LVDS to DP display bridge)"); +MODULE_LICENSE("GPL v2"); -- 2.33.5
next prev parent reply other threads:[~2022-12-14 13:19 UTC|newest] Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top 2022-12-14 13:18 [d-kernel] [PATCH 01/32] clk: added Baikal-M clock management unit driver Alexey Sheplyakov 2022-12-14 13:18 ` [d-kernel] [PATCH 02/32] cpufreq-dt: don't load on Baikal-M SoC Alexey Sheplyakov 2022-12-14 13:18 ` [d-kernel] [PATCH 03/32] serial: 8250_dw: verify clock rate in dw8250_set_termios Alexey Sheplyakov 2022-12-14 13:18 ` [d-kernel] [PATCH 04/32] usb: dwc3: of-simple: added compatible string for Baikal-M SoC Alexey Sheplyakov 2022-12-14 13:18 ` [d-kernel] [PATCH 05/32] dw-pcie: refuse to load on Baikal-M with recent firmware Alexey Sheplyakov 2022-12-14 13:18 ` [d-kernel] [PATCH 06/32] arm64: Enable armv8 based Baikal-M SoC support Alexey Sheplyakov 2022-12-14 13:18 ` [d-kernel] [PATCH 07/32] efi-rtc: avoid calling efi.get_time on Baikal-M SoC Alexey Sheplyakov 2022-12-14 13:18 ` [d-kernel] [PATCH 08/32] arm64-stub: fixed secondary cores boot " Alexey Sheplyakov 2022-12-14 13:18 ` [d-kernel] [PATCH 09/32] pm: disable all sleep states on Baikal-M based boards Alexey Sheplyakov 2022-12-14 13:18 ` [d-kernel] [PATCH 10/32] net: fwnode_get_phy_id: consider all compatible strings Alexey Sheplyakov 2022-12-14 13:18 ` [d-kernel] [PATCH 11/32] net: stmmac: inital support of Baikal-T1/M SoCs GMAC Alexey Sheplyakov 2022-12-14 13:18 ` [d-kernel] [PATCH 12/32] dt-bindings: dwmac: Add bindings for Baikal-T1/M SoCs Alexey Sheplyakov 2022-12-14 13:19 ` [d-kernel] [PATCH 13/32] net: dwmac-baikal: added compatible strings Alexey Sheplyakov 2022-12-14 13:19 ` [d-kernel] [PATCH 14/32] Added TF307/TF306 board management controller driver Alexey Sheplyakov 2022-12-14 13:19 ` [d-kernel] [PATCH 15/32] hwmon: bt1-pvt: access registers via pvt_{readl, writel} helpers Alexey Sheplyakov 2022-12-14 13:19 ` [d-kernel] [PATCH 16/32] hwmon: bt1-pvt: define pvt_readl/pvt_writel for Baikal-M SoC Alexey Sheplyakov 2022-12-14 13:19 ` [d-kernel] [PATCH 17/32] hwmon: bt1-pvt: adjusted probing " Alexey Sheplyakov 2022-12-14 13:19 ` [d-kernel] [PATCH 18/32] hwmon: bt1-pvt: added compatible baikal, pvt Alexey Sheplyakov 2022-12-14 13:19 ` Alexey Sheplyakov [this message] 2022-12-14 13:19 ` [d-kernel] [PATCH 20/32] drm: added Baikal-M SoC video display unit driver Alexey Sheplyakov 2022-12-14 13:19 ` [d-kernel] [PATCH 21/32] drm/bridge: dw-hdmi: support ahb audio hw revision 0x2a Alexey Sheplyakov 2022-12-14 13:19 ` [d-kernel] [PATCH 22/32] dt-bindings: dw-hdmi: added ahb-audio-regshift Alexey Sheplyakov 2022-12-14 13:19 ` [d-kernel] [PATCH 23/32] drm/bridge: dw-hdmi: force ahb audio register offset for Baikal-M Alexey Sheplyakov 2022-12-14 13:19 ` [d-kernel] [PATCH 24/32] drm/panfrost: forcibly set dma-coherent on Baikal-M Alexey Sheplyakov 2022-12-14 13:19 ` [d-kernel] [PATCH 25/32] drm/panfrost: disable devfreq " Alexey Sheplyakov 2022-12-14 13:19 ` [d-kernel] [PATCH 26/32] ALSA: hda: Baikal-M support Alexey Sheplyakov 2022-12-14 13:19 ` [d-kernel] [PATCH 27/32] PCI: pcie-baikal: driver for Baikal-M with new firmware Alexey Sheplyakov 2022-12-14 13:19 ` [d-kernel] [PATCH 28/32] (BROKEN) dwc-i2s: support Baikal-M SoC Alexey Sheplyakov 2022-12-14 13:19 ` [d-kernel] [PATCH 29/32] input: added TF307 serio PS/2 emulator driver Alexey Sheplyakov 2022-12-14 13:19 ` [d-kernel] [PATCH 30/32] input: new driver - serdev-serio Alexey Sheplyakov 2022-12-14 13:19 ` [d-kernel] [PATCH 31/32] phy: realtek: leds configuration for RTL8211f Alexey Sheplyakov 2022-12-14 15:06 ` [d-kernel] [PATCH 01/32] clk: added Baikal-M clock management unit driver Vitaly Chikunov 2022-12-16 9:54 ` Alexey Sheplyakov 2022-12-16 12:34 ` Vitaly Chikunov 2022-12-16 12:40 ` Vitaly Chikunov
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20221214131919.681481-19-asheplyakov@basealt.ru \ --to=asheplyakov@basealt.ru \ --cc=devel-kernel@lists.altlinux.org \ --cc=jqt4@basealt.ru \ --cc=nir@basealt.ru \ --cc=rst@basealt.ru \ --cc=sin@basealt.ru \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
ALT Linux kernel packages development This inbox may be cloned and mirrored by anyone: git clone --mirror http://lore.altlinux.org/devel-kernel/0 devel-kernel/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 devel-kernel devel-kernel/ http://lore.altlinux.org/devel-kernel \ devel-kernel@altlinux.org devel-kernel@altlinux.ru devel-kernel@altlinux.com public-inbox-index devel-kernel Example config snippet for mirrors. Newsgroup available over NNTP: nntp://lore.altlinux.org/org.altlinux.lists.devel-kernel AGPL code for this site: git clone https://public-inbox.org/public-inbox.git