From e09b41010ba33a20a87472ee821fa407a5b8da36 Mon Sep 17 00:00:00 2001 From: José Pekkarinen Date: Mon, 11 Apr 2016 10:41:07 +0300 Subject: These changes are the raw update to linux-4.4.6-rt14. Kernel sources are taken from kernel.org, and rt patch from the rt wiki download page. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During the rebasing, the following patch collided: Force tick interrupt and get rid of softirq magic(I70131fb85). Collisions have been removed because its logic was found on the source already. Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769 Signed-off-by: José Pekkarinen --- kernel/drivers/media/platform/omap3isp/isp.c | 140 ++++----------------- kernel/drivers/media/platform/omap3isp/isp.h | 7 +- kernel/drivers/media/platform/omap3isp/ispcsiphy.h | 2 +- .../drivers/media/platform/omap3isp/isppreview.c | 4 - kernel/drivers/media/platform/omap3isp/ispstat.c | 5 +- kernel/drivers/media/platform/omap3isp/ispstat.h | 2 +- kernel/drivers/media/platform/omap3isp/ispvideo.c | 36 +++--- kernel/drivers/media/platform/omap3isp/ispvideo.h | 4 +- kernel/drivers/media/platform/omap3isp/omap3isp.h | 132 +++++++++++++++++++ 9 files changed, 182 insertions(+), 150 deletions(-) create mode 100644 kernel/drivers/media/platform/omap3isp/omap3isp.h (limited to 'kernel/drivers/media/platform/omap3isp') diff --git a/kernel/drivers/media/platform/omap3isp/isp.c b/kernel/drivers/media/platform/omap3isp/isp.c index 947d8be7b..56e683b19 100644 --- a/kernel/drivers/media/platform/omap3isp/isp.c +++ b/kernel/drivers/media/platform/omap3isp/isp.c @@ -101,7 +101,6 @@ static const struct isp_res_mapping isp_res_maps[] = { 0x0000, /* csi2a, len 0x0170 */ 0x0170, /* csiphy2, len 0x000c */ }, - .syscon_offset = 0xdc, .phy_type = ISP_PHY_TYPE_3430, }, { @@ -124,7 +123,6 @@ static const struct isp_res_mapping isp_res_maps[] = { 0x0570, /* csiphy1, len 0x000c */ 0x05c0, /* csi2c, len 0x0040 (2nd area) */ }, - .syscon_offset = 0x2f0, .phy_type = ISP_PHY_TYPE_3630, }, }; @@ -1796,47 +1794,6 @@ static void isp_unregister_entities(struct isp_device *isp) media_device_unregister(&isp->media_dev); } -/* - * isp_register_subdev - Register a sub-device - * @isp: OMAP3 ISP device - * @isp_subdev: platform data related to a sub-device - * - * Register an I2C sub-device which has not been registered by other - * means (such as the Device Tree). - * - * Return a pointer to the sub-device if it has been successfully - * registered, or NULL otherwise. - */ -static struct v4l2_subdev * -isp_register_subdev(struct isp_device *isp, - struct isp_platform_subdev *isp_subdev) -{ - struct i2c_adapter *adapter; - struct v4l2_subdev *sd; - - if (isp_subdev->board_info == NULL) - return NULL; - - adapter = i2c_get_adapter(isp_subdev->i2c_adapter_id); - if (adapter == NULL) { - dev_err(isp->dev, - "%s: Unable to get I2C adapter %d for device %s\n", - __func__, isp_subdev->i2c_adapter_id, - isp_subdev->board_info->type); - return NULL; - } - - sd = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter, - isp_subdev->board_info, NULL); - if (sd == NULL) { - dev_err(isp->dev, "%s: Unable to register subdev %s\n", - __func__, isp_subdev->board_info->type); - return NULL; - } - - return sd; -} - static int isp_link_entity( struct isp_device *isp, struct media_entity *entity, enum isp_interface_type interface) @@ -1910,8 +1867,6 @@ static int isp_link_entity( static int isp_register_entities(struct isp_device *isp) { - struct isp_platform_data *pdata = isp->pdata; - struct isp_platform_subdev *isp_subdev; int ret; isp->media_dev.dev = isp->dev; @@ -1968,42 +1923,9 @@ static int isp_register_entities(struct isp_device *isp) if (ret < 0) goto done; - /* - * Device Tree --- the external sub-devices will be registered - * later. The same goes for the sub-device node registration. - */ - if (isp->dev->of_node) - return 0; - - /* Register external entities */ - for (isp_subdev = pdata ? pdata->subdevs : NULL; - isp_subdev && isp_subdev->board_info; isp_subdev++) { - struct v4l2_subdev *sd; - - sd = isp_register_subdev(isp, isp_subdev); - - /* - * No bus information --- this is either a flash or a - * lens subdev. - */ - if (!sd || !isp_subdev->bus) - continue; - - sd->host_priv = isp_subdev->bus; - - ret = isp_link_entity(isp, &sd->entity, - isp_subdev->bus->interface); - if (ret < 0) - goto done; - } - - ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev); - done: - if (ret < 0) { + if (ret < 0) isp_unregister_entities(isp); - v4l2_async_notifier_unregister(&isp->notifier); - } return ret; } @@ -2404,37 +2326,24 @@ static int isp_probe(struct platform_device *pdev) return -ENOMEM; } - if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { - ret = of_property_read_u32(pdev->dev.of_node, "ti,phy-type", - &isp->phy_type); - if (ret) - return ret; + ret = of_property_read_u32(pdev->dev.of_node, "ti,phy-type", + &isp->phy_type); + if (ret) + return ret; - isp->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, - "syscon"); - if (IS_ERR(isp->syscon)) - return PTR_ERR(isp->syscon); + isp->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "syscon"); + if (IS_ERR(isp->syscon)) + return PTR_ERR(isp->syscon); - ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, - &isp->syscon_offset); - if (ret) - return ret; + ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, + &isp->syscon_offset); + if (ret) + return ret; - ret = isp_of_parse_nodes(&pdev->dev, &isp->notifier); - if (ret < 0) - return ret; - ret = v4l2_async_notifier_register(&isp->v4l2_dev, - &isp->notifier); - if (ret) - return ret; - } else { - isp->pdata = pdev->dev.platform_data; - isp->syscon = syscon_regmap_lookup_by_pdevname("syscon.0"); - if (IS_ERR(isp->syscon)) - return PTR_ERR(isp->syscon); - dev_warn(&pdev->dev, - "Platform data support is deprecated! Please move to DT now!\n"); - } + ret = isp_of_parse_nodes(&pdev->dev, &isp->notifier); + if (ret < 0) + return ret; isp->autoidle = autoidle; @@ -2513,11 +2422,6 @@ static int isp_probe(struct platform_device *pdev) goto error_isp; } - if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node) { - isp->syscon_offset = isp_res_maps[m].syscon_offset; - isp->phy_type = isp_res_maps[m].phy_type; - } - for (i = 1; i < OMAP3_ISP_IOMEM_CSI2A_REGS1; i++) isp->mmio_base[i] = isp->mmio_base[0] + isp_res_maps[m].offset[i]; @@ -2557,18 +2461,24 @@ static int isp_probe(struct platform_device *pdev) if (ret < 0) goto error_iommu; - isp->notifier.bound = isp_subdev_notifier_bound; - isp->notifier.complete = isp_subdev_notifier_complete; - ret = isp_register_entities(isp); if (ret < 0) goto error_modules; + isp->notifier.bound = isp_subdev_notifier_bound; + isp->notifier.complete = isp_subdev_notifier_complete; + + ret = v4l2_async_notifier_register(&isp->v4l2_dev, &isp->notifier); + if (ret) + goto error_register_entities; + isp_core_init(isp, 1); omap3isp_put(isp); return 0; +error_register_entities: + isp_unregister_entities(isp); error_modules: isp_cleanup_modules(isp); error_iommu: diff --git a/kernel/drivers/media/platform/omap3isp/isp.h b/kernel/drivers/media/platform/omap3isp/isp.h index e57994317..5acc2e651 100644 --- a/kernel/drivers/media/platform/omap3isp/isp.h +++ b/kernel/drivers/media/platform/omap3isp/isp.h @@ -17,7 +17,6 @@ #ifndef OMAP3_ISP_CORE_H #define OMAP3_ISP_CORE_H -#include #include #include #include @@ -27,6 +26,7 @@ #include #include +#include "omap3isp.h" #include "ispstat.h" #include "ispccdc.h" #include "ispreg.h" @@ -101,15 +101,11 @@ struct regmap; * struct isp_res_mapping - Map ISP io resources to ISP revision. * @isp_rev: ISP_REVISION_x_x * @offset: register offsets of various ISP sub-blocks - * @syscon_offset: offset of the syscon register for 343x / 3630 - * (CONTROL_CSIRXFE / CONTROL_CAMERA_PHY_CTRL, respectively) - * from the syscon base address * @phy_type: ISP_PHY_TYPE_{3430,3630} */ struct isp_res_mapping { u32 isp_rev; u32 offset[OMAP3_ISP_IOMEM_LAST]; - u32 syscon_offset; u32 phy_type; }; @@ -184,7 +180,6 @@ struct isp_device { u32 revision; /* platform HW resources */ - struct isp_platform_data *pdata; unsigned int irq_num; void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST]; diff --git a/kernel/drivers/media/platform/omap3isp/ispcsiphy.h b/kernel/drivers/media/platform/omap3isp/ispcsiphy.h index e17c88bea..28b63b28f 100644 --- a/kernel/drivers/media/platform/omap3isp/ispcsiphy.h +++ b/kernel/drivers/media/platform/omap3isp/ispcsiphy.h @@ -17,7 +17,7 @@ #ifndef OMAP3_ISP_CSI_PHY_H #define OMAP3_ISP_CSI_PHY_H -#include +#include "omap3isp.h" struct isp_csi2_device; struct regulator; diff --git a/kernel/drivers/media/platform/omap3isp/isppreview.c b/kernel/drivers/media/platform/omap3isp/isppreview.c index 15cb254cc..13803270d 100644 --- a/kernel/drivers/media/platform/omap3isp/isppreview.c +++ b/kernel/drivers/media/platform/omap3isp/isppreview.c @@ -929,14 +929,10 @@ static void preview_setup_hw(struct isp_prev_device *prev, u32 update, u32 active) { unsigned int i; - u32 features; if (update == 0) return; - features = (prev->params.params[0].features & active) - | (prev->params.params[1].features & ~active); - for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { const struct preview_update *attr = &update_attrs[i]; struct prev_params *params; diff --git a/kernel/drivers/media/platform/omap3isp/ispstat.c b/kernel/drivers/media/platform/omap3isp/ispstat.c index 20434e83e..94d4c295d 100644 --- a/kernel/drivers/media/platform/omap3isp/ispstat.c +++ b/kernel/drivers/media/platform/omap3isp/ispstat.c @@ -235,7 +235,7 @@ static int isp_stat_buf_queue(struct ispstat *stat) if (!stat->active_buf) return STAT_NO_BUF; - ktime_get_ts(&stat->active_buf->ts); + v4l2_get_timestamp(&stat->active_buf->ts); stat->active_buf->buf_size = stat->buf_size; if (isp_stat_buf_check_magic(stat, stat->active_buf)) { @@ -496,8 +496,7 @@ int omap3isp_stat_request_statistics(struct ispstat *stat, return PTR_ERR(buf); } - data->ts.tv_sec = buf->ts.tv_sec; - data->ts.tv_usec = buf->ts.tv_nsec / NSEC_PER_USEC; + data->ts = buf->ts; data->config_counter = buf->config_counter; data->frame_number = buf->frame_number; data->buf_size = buf->buf_size; diff --git a/kernel/drivers/media/platform/omap3isp/ispstat.h b/kernel/drivers/media/platform/omap3isp/ispstat.h index b79380d83..6d9b0244f 100644 --- a/kernel/drivers/media/platform/omap3isp/ispstat.h +++ b/kernel/drivers/media/platform/omap3isp/ispstat.h @@ -39,7 +39,7 @@ struct ispstat_buffer { struct sg_table sgt; void *virt_addr; dma_addr_t dma_addr; - struct timespec ts; + struct timeval ts; u32 buf_size; u32 frame_number; u16 config_counter; diff --git a/kernel/drivers/media/platform/omap3isp/ispvideo.c b/kernel/drivers/media/platform/omap3isp/ispvideo.c index d285af18d..f4f591652 100644 --- a/kernel/drivers/media/platform/omap3isp/ispvideo.c +++ b/kernel/drivers/media/platform/omap3isp/ispvideo.c @@ -320,7 +320,7 @@ isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh) */ static int isp_video_queue_setup(struct vb2_queue *queue, - const struct v4l2_format *fmt, + const void *parg, unsigned int *count, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { @@ -342,8 +342,9 @@ static int isp_video_queue_setup(struct vb2_queue *queue, static int isp_video_buffer_prepare(struct vb2_buffer *buf) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(buf); struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue); - struct isp_buffer *buffer = to_isp_buffer(buf); + struct isp_buffer *buffer = to_isp_buffer(vbuf); struct isp_video *video = vfh->video; dma_addr_t addr; @@ -363,7 +364,8 @@ static int isp_video_buffer_prepare(struct vb2_buffer *buf) return -EINVAL; } - vb2_set_plane_payload(&buffer->vb, 0, vfh->format.fmt.pix.sizeimage); + vb2_set_plane_payload(&buffer->vb.vb2_buf, 0, + vfh->format.fmt.pix.sizeimage); buffer->dma = addr; return 0; @@ -380,8 +382,9 @@ static int isp_video_buffer_prepare(struct vb2_buffer *buf) */ static void isp_video_buffer_queue(struct vb2_buffer *buf) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(buf); struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue); - struct isp_buffer *buffer = to_isp_buffer(buf); + struct isp_buffer *buffer = to_isp_buffer(vbuf); struct isp_video *video = vfh->video; struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); enum isp_pipeline_state state; @@ -392,7 +395,7 @@ static void isp_video_buffer_queue(struct vb2_buffer *buf) spin_lock_irqsave(&video->irqlock, flags); if (unlikely(video->error)) { - vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buffer->vb.vb2_buf, VB2_BUF_STATE_ERROR); spin_unlock_irqrestore(&video->irqlock, flags); return; } @@ -464,7 +467,7 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video) list_del(&buf->irqlist); spin_unlock_irqrestore(&video->irqlock, flags); - v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&buf->vb.timestamp); /* Do frame number propagation only if this is the output video node. * Frame number either comes from the CSI receivers or it gets @@ -473,15 +476,15 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video) * first, so the input number might lag behind by 1 in some cases. */ if (video == pipe->output && !pipe->do_propagation) - buf->vb.v4l2_buf.sequence = + buf->vb.sequence = atomic_inc_return(&pipe->frame_number); else - buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number); + buf->vb.sequence = atomic_read(&pipe->frame_number); if (pipe->field != V4L2_FIELD_NONE) - buf->vb.v4l2_buf.sequence /= 2; + buf->vb.sequence /= 2; - buf->vb.v4l2_buf.field = pipe->field; + buf->vb.field = pipe->field; /* Report pipeline errors to userspace on the capture device side. */ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) { @@ -491,7 +494,7 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video) state = VB2_BUF_STATE_DONE; } - vb2_buffer_done(&buf->vb, state); + vb2_buffer_done(&buf->vb.vb2_buf, state); spin_lock_irqsave(&video->irqlock, flags); @@ -546,7 +549,7 @@ void omap3isp_video_cancel_stream(struct isp_video *video) buf = list_first_entry(&video->dmaqueue, struct isp_buffer, irqlist); list_del(&buf->irqlist); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } video->error = true; @@ -1018,8 +1021,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) pipe->entities = 0; - if (video->isp->pdata && video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, true); + /* TODO: Implement PM QoS */ pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); pipe->max_rate = pipe->l3_ick; @@ -1100,8 +1102,7 @@ err_set_stream: err_check_format: media_entity_pipeline_stop(&video->video.entity); err_pipeline_start: - if (video->isp->pdata && video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, false); + /* TODO: Implement PM QoS */ /* The DMA queue must be emptied here, otherwise CCDC interrupts that * will get triggered the next time the CCDC is powered up will try to * access buffers that might have been freed but still present in the @@ -1161,8 +1162,7 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) video->queue = NULL; video->error = false; - if (video->isp->pdata && video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, false); + /* TODO: Implement PM QoS */ media_entity_pipeline_stop(&video->video.entity); done: diff --git a/kernel/drivers/media/platform/omap3isp/ispvideo.h b/kernel/drivers/media/platform/omap3isp/ispvideo.h index 4071dd706..bcf0e0acc 100644 --- a/kernel/drivers/media/platform/omap3isp/ispvideo.h +++ b/kernel/drivers/media/platform/omap3isp/ispvideo.h @@ -20,7 +20,7 @@ #include #include #include -#include +#include #define ISP_VIDEO_DRIVER_NAME "ispvideo" #define ISP_VIDEO_DRIVER_VERSION "0.0.2" @@ -122,7 +122,7 @@ static inline int isp_pipeline_ready(struct isp_pipeline *pipe) * @dma: DMA address */ struct isp_buffer { - struct vb2_buffer vb; + struct vb2_v4l2_buffer vb; struct list_head irqlist; dma_addr_t dma; }; diff --git a/kernel/drivers/media/platform/omap3isp/omap3isp.h b/kernel/drivers/media/platform/omap3isp/omap3isp.h new file mode 100644 index 000000000..190e259a6 --- /dev/null +++ b/kernel/drivers/media/platform/omap3isp/omap3isp.h @@ -0,0 +1,132 @@ +/* + * omap3isp.h + * + * TI OMAP3 ISP - Bus Configuration + * + * Copyright (C) 2011 Nokia Corporation + * + * Contacts: Laurent Pinchart + * Sakari Ailus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __OMAP3ISP_H__ +#define __OMAP3ISP_H__ + +enum isp_interface_type { + ISP_INTERFACE_PARALLEL, + ISP_INTERFACE_CSI2A_PHY2, + ISP_INTERFACE_CCP2B_PHY1, + ISP_INTERFACE_CCP2B_PHY2, + ISP_INTERFACE_CSI2C_PHY1, +}; + +/** + * struct isp_parallel_cfg - Parallel interface configuration + * @data_lane_shift: Data lane shifter + * 0 - CAMEXT[13:0] -> CAM[13:0] + * 1 - CAMEXT[13:2] -> CAM[11:0] + * 2 - CAMEXT[13:4] -> CAM[9:0] + * 3 - CAMEXT[13:6] -> CAM[7:0] + * @clk_pol: Pixel clock polarity + * 0 - Sample on rising edge, 1 - Sample on falling edge + * @hs_pol: Horizontal synchronization polarity + * 0 - Active high, 1 - Active low + * @vs_pol: Vertical synchronization polarity + * 0 - Active high, 1 - Active low + * @fld_pol: Field signal polarity + * 0 - Positive, 1 - Negative + * @data_pol: Data polarity + * 0 - Normal, 1 - One's complement + */ +struct isp_parallel_cfg { + unsigned int data_lane_shift:2; + unsigned int clk_pol:1; + unsigned int hs_pol:1; + unsigned int vs_pol:1; + unsigned int fld_pol:1; + unsigned int data_pol:1; +}; + +enum { + ISP_CCP2_PHY_DATA_CLOCK = 0, + ISP_CCP2_PHY_DATA_STROBE = 1, +}; + +enum { + ISP_CCP2_MODE_MIPI = 0, + ISP_CCP2_MODE_CCP2 = 1, +}; + +/** + * struct isp_csiphy_lane: CCP2/CSI2 lane position and polarity + * @pos: position of the lane + * @pol: polarity of the lane + */ +struct isp_csiphy_lane { + u8 pos; + u8 pol; +}; + +#define ISP_CSIPHY1_NUM_DATA_LANES 1 +#define ISP_CSIPHY2_NUM_DATA_LANES 2 + +/** + * struct isp_csiphy_lanes_cfg - CCP2/CSI2 lane configuration + * @data: Configuration of one or two data lanes + * @clk: Clock lane configuration + */ +struct isp_csiphy_lanes_cfg { + struct isp_csiphy_lane data[ISP_CSIPHY2_NUM_DATA_LANES]; + struct isp_csiphy_lane clk; +}; + +/** + * struct isp_ccp2_cfg - CCP2 interface configuration + * @strobe_clk_pol: Strobe/clock polarity + * 0 - Non Inverted, 1 - Inverted + * @crc: Enable the cyclic redundancy check + * @ccp2_mode: Enable CCP2 compatibility mode + * ISP_CCP2_MODE_MIPI - MIPI-CSI1 mode + * ISP_CCP2_MODE_CCP2 - CCP2 mode + * @phy_layer: Physical layer selection + * ISP_CCP2_PHY_DATA_CLOCK - Data/clock physical layer + * ISP_CCP2_PHY_DATA_STROBE - Data/strobe physical layer + * @vpclk_div: Video port output clock control + */ +struct isp_ccp2_cfg { + unsigned int strobe_clk_pol:1; + unsigned int crc:1; + unsigned int ccp2_mode:1; + unsigned int phy_layer:1; + unsigned int vpclk_div:2; + struct isp_csiphy_lanes_cfg lanecfg; +}; + +/** + * struct isp_csi2_cfg - CSI2 interface configuration + * @crc: Enable the cyclic redundancy check + */ +struct isp_csi2_cfg { + unsigned crc:1; + struct isp_csiphy_lanes_cfg lanecfg; +}; + +struct isp_bus_cfg { + enum isp_interface_type interface; + union { + struct isp_parallel_cfg parallel; + struct isp_ccp2_cfg ccp2; + struct isp_csi2_cfg csi2; + } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */ +}; + +#endif /* __OMAP3ISP_H__ */ -- cgit 1.2.3-korg